AMPED / HomeHeroes · replaces LeadByte · v2 pacing queue

How the router decides — every branch, lead to assignment

One decision per lead: which buyer gets it. Everything's on this page — the full decision map (every branch and layer a lead passes through, below), the live step-through where you watch the queue reorder lead by lead, and the calculator where you drag weights and coverage and watch the distribution respond. Start with the map, then drive the logic yourself.

When a lead comes in

The full decision path — every branch & layer, to assignment
A lead is born from an ad, hits a lander, and enters here with a zip + offer type. Follow the arrows: it's located to a county group, filtered by eligibility, then ranked by pacing deficit — the one new piece. Diamonds are branches; the dashed arrow is the pacing loop that feeds the next lead's math.
flowchart TB
  start(["Lead submitted
(zip + offer type)"]):::start subgraph L["1 · LOCATE"] direction TB county["Derive county from ZIP
(USPS crosswalk)"]:::proc group["Place in that county's GROUP"]:::proc end subgraph F["2 · FILTER — eligibility gate"] direction TB gate[/"Keep only buyers passing ALL:
active · serves zip · buys offer · credits ≥ price
· under caps · in schedule · filters"/]:::gate elig{"Any eligible
buyers?"}:::dec end subgraph R["3 · RANK — pacing deficit (the brain)"] direction TB over{"Manual override
buyer eligible?"}:::dec cap["Cap targets at coverage ceiling
effShare = waterfill(weight, ceiling)"]:::proc calc["Prospective deficit per buyer:
+= effShare each lead · −1 on win
(new buyer starts at par)"]:::proc pin{"Buyer pinned an
offer mix?"}:::dec smin["Offer token-bucket guard:
keep if EV ≤ mix·received + burst"]:::proc sdef["(no offer cap)"]:::proc rank["Pick MAX NORMALIZED deficit
deficit ÷ effShare
ties: longest-wait ↑ · weight ↓ · id"]:::proc win["Winner = #1"]:::proc end house(["House-buyer fallback
(never dropped)"]):::term assign(["Assign + debit
(atomic · fully logged)"]):::assign start --> county --> group --> gate --> elig elig -- "No" --> house elig -- "Yes" --> over over -- "Yes (force)" --> win over -- "No" --> cap --> calc --> pin pin -- "Yes" --> smin --> rank pin -- "No" --> sdef --> rank rank --> win --> assign assign -. "−1 winner · += effShare each lead
(prospective, per-buyer epoch)" .-> calc classDef start fill:#12294a,stroke:#2f81f7,stroke-width:2px,color:#eaf1fb; classDef proc fill:#16233b,stroke:#334867,color:#eaf1fb; classDef dec fill:#2a2416,stroke:#ffb020,stroke-width:2px,color:#ffe6bd; classDef gate fill:#1c2a12,stroke:#c6f24e,stroke-width:2px,color:#eef3d9; classDef assign fill:#123227,stroke:#3ad29f,stroke-width:2px,color:#d6f7ec; classDef term fill:#2c1620,stroke:#ff5c72,stroke-width:2px,color:#ffd7dd;

The one idea

The queue is the priority. A buyer that can't get leads (small coverage) sees its deficit grow, floating it to the top — so it wins its next eligible lead automatically. No manual "priority spot."

Deficit — dial ①

# cap target at coverage ceiling effShare = waterfill(weight, ceil) deficit = effShare×N − received pick: max deficit ÷ effShare # normalized → spill by weight

Offer mix — dial ②

# token bucket on ACTUAL intake keep b if: gotEV+1 ≤ mix×(got+1) + burst # infeasible → least over-ratio

Tie-breaks

  1. bigger normalized deficit
  2. longest since last lead
  3. bigger weight
  4. buyer id (stable)

Legend

start / lead in
step (compute)
decision / branch
eligibility filter
assign + debit
fallback (house)

Step 3 — Eligibility gate

A hard yes/no filter, before anyone is ranked
Ranking never sees a buyer who fails here. This is the same gate the engine already has — unchanged. A buyer must pass every check to make the queue.
CheckPasses when…Notes
Activebuyer + their routing profile are on, not pauseda paused buyer is invisible
Zip (geo)a coverage row matches the lead's zip (exact / prefix / region)zero coverage rows = covers nothing
Offerbuyer buys this service type & has a price rowmissing price = ineligible (not a free lead)
Creditsprepaid balance ≥ the lead's priceprepay model, no overdraft
Capsunder daily / weekly / monthly limitbuyer-local calendar windows
Schedulenow is inside the buyer's hours/daysbad timezone fails closed
Filtersevery per-buyer rule passes (homeowner-only, etc.)AND of all rules

Step 4 — The deficit queue (the brain)

Watch it think, lead by lead
This is the whole change from today's engine. Below is the live SD group. Hit Next lead and watch each buyer's deficit (how many leads they're owed) update, the queue reorder, and the winner get picked. Notice how Voltwell — only 18 zips — keeps climbing while it's ineligible, then wins the instant one of its zips comes up.
# per buyer, inside the group effShare = waterfill(weight, coverage-ceiling) # cap at what its zips supply deficit = effShare × N − received # + = behind, − = ahead winner = eligible buyer with the biggest deficit ÷ effShare (normalized → spill by weight; ties → longest wait → weight → id)
Press Next lead to route the first lead.
BuyerWeightZipsReceived TargetDeficitEligible?
Cumulative share vs 30-day target

❌ Static weighting (LeadByte)

Picks by a fixed weight % every lead, independently. It never notices Voltwell falling behind because its zips are rare — so Voltwell gets ~1–2% instead of its 16%. A "priority spot" is the manual band-aid for this blindness.

✅ Deficit queue

Every lead Voltwell can't get grows its deficit, floating it to the top. The next lead in its 18 zips → Voltwell is #1 and wins. It catches up to the ceiling of what's physically available, automatically. The queue is the priority.

The second dial — offer mix

One queue, but each buyer can pin panel vs EV
Total weight decides who's next. Offer mix is a secondary cap so a buyer isn't flooded with low-quality EV beyond their ad ratio. It stays one queue — not separate per-offer queues (which would skew total volume for buyers who take both).
# buyer pinned 75% panel / 25% EV # token bucket on ACTUAL leads received: keep b for an EV lead iff receivedEV + 1 ≤ mix·(received + 1) + burst # at their real EV ratio → guard fails → # the EV lead flows to a buyer who still wants it # infeasible pins → least-over-ratio wins # no pin → always passes (natural mix)

Why not just split into two queues?

Because a buyer who accepts both panel and EV would then sit in both and pull double volume vs a single-offer buyer next in line. Keeping one queue with a per-offer cap preserves each buyer's total share while still honoring their mix. The simulator below has an offer-mix toggle so you can see it.

Interactive simulator

Drag the dials, run leads, read the result
Change each buyer's weight, coverage size, and offer pin; change the lead supply; run the stream through naïve weighting vs the deficit queue side by side. The grey tick on each bar is the buyer's weight target; the green tick is their supply ceiling (the max they could ever win given coverage).
Buyers (SD group)
Lead supply
Lower "volume in Voltwell's zips" = more leads land outside its area = harder to hit its weight (that's Anthony's case). Raise it and its supply ceiling rises.

Tie-breaks, edge cases & operator controls

The corners — so there are no surprises

⚖ Ties

Equal deficit → longest since last lead (round-robin) → bigger weight → id. Fully deterministic; shadow-mode reproduces exactly.

🧊 Cold start

Empty window → everyone deficit 0 → falls back to round-robin by weight. Converges within ~2× the buyer count.

🈳 Nobody eligible

No coverage / all capped / all out of credits → house-buyer fallback. A lead is never dropped.

🎯 Manual override

"Feed buyer X everything it can take until I turn it off." Force-assigns any lead they're eligible for; the rest fall through to the queue.

🔥 Burn budget fast

Set a buyer's weight above their budget share → the deficit naturally over-delivers to them. (Point Loma at 52%.)

⛰ Supply ceiling

A buyer can't win more than their zips produce. If Voltwell's zips carry 10%, 10% is success — 16% is physically impossible.

🔀 Concurrency

Two leads racing for the same #1 are serialized by the atomic assign; the loser re-ranks against fresh counts.

🕶 Shadow first

Runs beside LeadByte logging what it would do, ≥2 weeks, before any cutover. Zero risk to live routing.

🔁 Overflow

The share a low-coverage buyer can't absorb flows to the all-county catch-all by weight. Nothing is stranded.

Open decisions to lock

Most now locked after the adversarial review
D-1 · Overload split — LOCKED: normalized
When a small buyer can't hit its target, the leftover spills proportional to weight (normalized deficit ÷ effShare), not equal-per-buyer. Matthew + both reviewers agreed. raw stays only as a config escape hatch.
D-2 · Pin semantics + infeasibility
A pin means "≤ X% of the leads you actually receive are EV" (token bucket). If pins are collectively infeasible vs supply, surplus EV goes to the least-over-ratio buyer and ops gets an infeasibility alert. Confirm the wording to buyers.
D-3 · Pacing window
30 days rolling to start (per-funnel knob); shorter reacts faster but noisier. The deficit is a maintained per-buyer counter (prospective, epoch-reset on config change) — not recomputed against the raw window.
D-4 · Accelerated pacing
Weight inflation now means "elevated share from now" (no back-pay burst) — enough for "burn fast." Defer a separate spend-by-date pacer.
D-5 · Refund / dispute accounting
A credited-back lead does not decrement received (the opportunity was consumed; else disputing becomes a queue-jump lever). Confirm.
Same engine underneath: uses buyer_routing_profiles.weight (already in the schema), county groups ride the existing region coverage, every decision writes a full trace to routing_decisions. The only real change is the ranking layer. Algorithm family: Weighted Deficit Round Robin (the fair-share scheduler that runs internet routers), with a hard eligibility gate. This page's step-through and simulator run the exact logic from sim.mjs.