Home-Run Routing: Teaching a System to Trace a Circuit Back to Its Panel

Blog
Home-Run Routing: Teaching a System to Trace a Circuit Back to Its Panel

Counting receptacles is the easy half of an electrical takeoff. The hard half is the wire: every device has to trace back to a circuit, and every circuit back to a panel, across a drawing that never says so in one place. This is how we taught the system to make that trace — and, more to the point, what it does on the pages where it can’t.

A home run is the path from a device to the panel that feeds it

A home run is the path from a device to the panel that feeds it. An estimator routes that path in their head — or takes an average measurement to a few devices and applies it across the rest. Neither is precise, and on a dense sheet there’s far too much to hold in your head at once.

If you ask an electrical estimator what actually slows them down, they don’t say counting outlets. They say the home runs. A home run is the wire path from a device — a receptacle, a light, a junction box — back to the breaker in the panel that powers it. A takeoff isn’t finished when the devices are counted. It’s finished when every device is tied to a circuit, every circuit to a panel, and the wire between them is measured. That tracing is most of the labor, and it’s the part that resisted automation the longest, because the information you need to do it is scattered: the device is on the floor plan, the circuit lives in the panel schedule, and the panel itself might be drawn on a third sheet.

So the real problem in an electrical takeoff was never detection. It’s routing. We spent most of our effort there, and most of what we learned came from watching the routing fail.

What the routing step actually does

We built routing as its own scope that runs on a page — internally, the home-run-routing scope, with a sibling, home-run-routing-jbox, for the junction-box variant. When it runs, the takeoff service tries to detect the devices on the sheet, route each one back to its panel, and report how many circuits it managed to route.

That last part — the count of what it routed — turned out to be the hinge the whole feature swings on. The result summary carries a single field, total_circuits_routed. On a clean sheet with legible symbols and a readable panel reference, that number comes back populated and the estimator gets routed home runs without drawing a line. That’s the happy path, and on good drawings it’s most of the work gone.

The routing scope returns one number that decides everything downstream

The routing scope returns one number that decides everything downstream: how many circuits it actually routed.

The part we think about most is what happens when it fails

Auto-detection doesn’t always work. The symbols can be ambiguous, the panel reference unreadable, the sheet a scan of a scan. A system facing that has two tempting and bad options: return nothing and let the estimator discover the gap later, or guess and produce confident, wrong home runs. On an electrical takeoff the second is worse than the first, because a wrong circuit assignment is the kind of error that looks authoritative and survives all the way into a bid.

We took a third path, and it’s the decision we’re most deliberate about. When the routing scope comes back with total_circuits_routed equal to zero, the system reads that as auto-detect found nothing here and opens a Panel/Circuit selection modal — handing the estimator a fast manual path on exactly the page where the automation gave up, rather than failing quietly.

What we care about is the discipline around when that fallback fires. It triggers only on a genuine numeric zero. A missing count, a non-numeric count, a null result, a response that belongs to a different scope — all of those are treated as no signal, not no circuits, and the modal stays closed. We’d rather not interrupt an estimator on an ambiguous reading than interrupt them on a false alarm. That single distinction — a true zero is information; everything else is silence — is most of what makes the feature feel trustworthy in daily use.

Only a genuine numeric zero opens the fallback

Only a genuine numeric zero opens the fallback. Missing, null, non-numeric, or wrong-scope results are treated as “no signal,” not “no circuits.”

The unglamorous detail that makes it usable: a loop guard

There’s a failure mode here that never shows up in a demo and shows up immediately in real use. The fallback fires on a zero. The estimator picks a panel and circuit, and the system resubmits the routing. If that resubmit also comes back zero, the modal would pop again — and the estimator would be trapped, re-prompted forever on a page the model simply can’t route.

So the routing keeps a small piece of memory: which pages have already been through a fallback in the current cycle, keyed by page and scope — because standard and junction-box routing can run independently on the same sheet, and we didn’t want one to suppress the other. Once a page has been through the fallback, a second zero on the resubmit won’t re-prompt. The guard clears only when the estimator starts a fresh takeoff on that page; one explicit Takeoff click resets it. The rule we held to is deliberately plain: one fallback prompt per submit, never a loop.

There’s a smaller detail underneath that one, and we keep it because it’s the kind of thing that only earns its place by going wrong first. That guard lives in module-level state, not in component state — because during development the hot-reload could wipe a component reference mid-cycle, and the reference would read back as null right after being set, which quietly reopened the infinite loop the guard existed to prevent. Module state survives the reload. It’s a tiny thing. It’s also exactly the kind of tiny thing that separates a feature that works in a recording from one that works on a Tuesday afternoon with a real set open.

Reading the panel schedule — and knowing when not to guess

Routing a device to a panel is only half-useful if you can’t read what the panel schedule contains. Panel schedules are tables, and their column headers are never standardized. One drawing labels a column one way, the next labels the same column another, and the layout shifts from sheet to sheet. So the schedule reader maps headers by family rather than by exact string, and it does it most-specific-first — claiming the more specific column before a bare, generic header can grab it, folding the variants that mean the same thing into one canonical column. Each rule carries an exclude list, too, so a header won’t get swallowed by a looser one that happens to share a word — because the failures we kept seeing were greedy matches, not missed ones. First applicable rule wins.

The more interesting half is what we deliberately didn’t make fuzzy. Circuit and panel — the two columns that decide which breaker a wire ties to — are read through a small, explicit alias table: circuit, ckt, circuit_no; panel, pnl. We kept that list intentionally narrow, because substring matching there is dangerous in a way it isn’t elsewhere. A loose match would read a “circuit_breaker_type” column as a circuit value and route the wire to the wrong place with full confidence. So the same system is fuzzy where fuzziness is safe and strict where it isn’t. That split isn’t an accident of two different authors; it’s the whole philosophy in miniature — guess generously on labels, never guess on the wire.

Circuit and panel columns are read through a deliberately narrow alias table

Panel-schedule headers map by family, most-specific-first. Circuit and panel are read through a deliberately narrow alias table — strict where a wrong guess would misroute a wire.

Why this is the shape of electrical AI worth building

For someone deciding whether this is a category worth backing, the takeaway isn’t “we route home runs.” It’s what the design says about how something like this gets built to last.

The easy version of electrical AI is a device detector with a confidence score and a clean-sheet demo. It falls apart in production, because real electrical sets aren’t clean and the cost of a wrong circuit assignment is high enough that estimators won’t trust a system that hides its failures. The version that earns trust is the quieter one: it automates the trace where it can, says so plainly where it can’t, hands you a fast manual path on exactly that page, refuses to interrupt you on an ambiguous signal, and is strict precisely where a confident wrong answer would do the most damage.

None of that is a model capability. It’s product judgment wired into the routing layer — treat a true zero differently from no-signal, give one fallback per attempt, never loop, read the messy panel-schedule headers by family but the circuit column by an exact list. That judgment is the part a competitor copying the detector can’t copy, because it only exists because we ran the thing against real drawings and paid attention to where it broke.

The counting was never the hard part. The wire is the product.