A cross-trade duplicate is the same physical work billed under two different trades or two different vendors. The pattern survives most bookkeeping because the line items look legitimate in isolation: two different subs, two different cost codes, two different invoice numbers, two different scope descriptions. Only when someone reads the scopes side by side does it become obvious that both trades billed for the same wall, the same vent, or the same slab. These are the errors that quietly inflate residential budgets by 1–3% and the ones that show up at tax time, when fixing them costs a lot more than catching them at draw entry.
Why this happens
Residential trade scopes blur at the edges. The electrician and the low-voltage sub both pull wires, and the line between “line voltage” and “low voltage” depends on which sub agreed to do which run. The framer and the trim carpenter both touch windows; the framer sets the rough opening and the trim carpenter does the casing, and the labor scope between them is negotiated on every project. The plumber and the HVAC sub both run vent stacks. The drywall sub and the painter both prep walls. None of these handoffs are inherently confusing, but the scope language in the contracts is often loose, and that looseness shows up later in the form of two invoices for the same task.
The other source is buyer change orders. The owner asks for a wired Cat6 run during framing, the electrician adds it to scope, the low-voltage sub later sees the run on the plans and bills for the same Cat6 because nobody told the low-voltage sub that the electrician took it. The original electrical contract was never adjusted to reflect the change, so neither sub knows the other is billing for the same wire.
Real examples from residential
These are the specific cross-trade duplicates we see on small residential builds, in rough order of frequency:
- Bath fan venting. The HVAC sub bills the duct run from the fan to the soffit; the plumber also bills “vent stack labor” on the same line. Both invoices reference the same physical penetration. The scope looks different on paper (HVAC says “exhaust duct,” plumber says “vent line”) but the work is one duct run.
- Window install. The framer bills “window sets” labor (ten windows at one hour each); the trim sub bills “window casing labor” (ten windows at one and a half hours each) with no clear line between “set” and “case.” Without a documented scope split, both subs assume the work between rough opening and finished casing is theirs.
- Concrete. The foundation sub bills the slab on grade; the flatwork sub also bills “interior slab” for the garage. The garage slab is the same pour as the rest of the foundation, the flatwork sub was supposed to do exterior flatwork only, and the cost code on the flatwork bill is mis-assigned.
- Drywall vs. paint. The drywall sub bills “skim coat” on accent walls; the painter bills “wall prep” on the same accent walls. Both lines are for the same surface preparation, performed once.
- Demolition during a remodel. The general carpenter bills demo as part of a framing line; a separate demo sub also bills the same demo. This is rare on new construction and very common on additions.
Why naive duplicate detection misses these
The simple version of duplicate detection looks for the same vendor billed twice on the same draw with the same amount. That check catches accidental double-entry and almost nothing else. The cross- trade duplicates above all involve two different vendors, two different cost codes, two different amounts, and two different scope descriptions. The naive check ignores 80% of the dollars at risk.
Useful detection runs three different patterns:
| Pattern | Detection layer | Catches |
|---|---|---|
| Same vendor, same draw, same amount | Rule engine | Accidental double-entry |
| Same vendor, same period, different amount | Rule engine with tolerance | Vendor billed twice with rounding or split invoice |
| Different vendors, overlapping scope keywords | LLM cross-check | Cross-trade duplicates with different language |
| Same task, different unit (labor hour vs. lump sum) | Rule engine + cost-code review | Lump-sum line that overlaps an hourly line |
Vendor name normalization
Half of the false negatives in duplicate detection come from vendor name variants. “ESJ Plumbing,” “ESJ Plumb,” and “Eastern States Joinery” can all be the same vendor under three different name strings, and the rule engine treats them as three vendors unless the names are normalized.
The structural fix is master-vendor enforcement at transaction entry. Every vendor exists exactly once in a normalized vendor record, every transaction is tied to that record by a foreign key, and free-text vendor names on incoming invoices get matched to the master record (with a fuzzy match for typos and a manual review step for genuinely new vendors). This is the same hygiene that prevents 1099 sprawl at year-end, and it pays for itself a second time at duplicate detection.
Cost code discipline
Every line item must be tied to a cost code, and every cost code must be tied to a single trade. Two lines under the same cost code for the same period flag for review. Two lines under different cost codes that historically overlap (drywall finish and paint prep, framing windows and trim windows) flag for cross-trade review.
The cost code structure is a one-time setup that pays back forever. On a small residential builder’s books, the right structure is roughly fifty top-level codes, each tied to a single trade, with sub-codes for material vs. labor vs. equipment. A line item with a loose cost code (“General contracting”) is the canary for an entry that will become a duplicate later.
Reconciliation cadence
When you find a duplicate matters as much as how you find it. The same duplicate has dramatically different costs to fix at three different cadences:
- Weekly review. The duplicate is caught while the relevant week is still fresh in the PM’s memory. The vendors are on site, the scope is current, and a phone call resolves the misallocation in fifteen minutes.
- Monthly review at draw time. The duplicate is caught when the draw package is assembled. The PM has to reconstruct the week from notes, and one of the two affected subs may already be off the project. Resolution takes a day, sometimes two, and often involves a credit on a future invoice rather than a clean reversal.
- Annual review at tax time. The duplicate is caught by the bookkeeper or accountant during 1099 prep. Both subs have been paid, both are off the project, and the PM has moved on to the next two builds. Resolution requires reissuing 1099s, processing a vendor refund or write-off, and explaining the discrepancy to the tax preparer. The administrative cost often exceeds the duplicate amount itself.
Weekly is the right cadence. Monthly is workable. Annual is the cadence at which duplicates become permanent.
Worked example: 926 Stratford
926 Stratford is a 1,784 SF spec build in Sweetwater, Tennessee, at a contract price of $430,250. During the framing phase, the general carpenter bills $4,200 for “rough framing labor, week of 04/14” under cost code 06-100. Two days later, the same general carpenter bills $4,180 for “framing labor, 4/14–4/18” under cost code 06-110.
The two invoices are for the same five days of work. Cost code 06-100 is “rough framing” and 06-110 is “framing labor,” which the office treats as different categories because the chart of accounts has both. The amounts differ by $20 because one bill rounds hours to the nearest half and the other rounds to the quarter. A naive duplicate check (same vendor, same amount) misses this. A vendor-name and cost-code-overlap check catches it on the same day the second invoice posts.
Caught at draw time, the resolution is a credit memo on the next invoice and an apology from the office. Caught at tax time, the resolution is a 1099 correction, a refund request, and a cost-code consolidation that should have happened a year earlier. The same $4,200 either disappears in a fifteen-minute phone call or generates a half-day of cleanup, depending on when the system flags it.
How BuilderGrid handles this
Every transaction is tied to a normalized vendor record by foreign key, so “ESJ Plumb” and “ESJ Plumbing” can only exist as the same vendor. Every line item is tied to a cost code, and the cost-code chart enforces a single trade per code. The rule engine flags same-vendor duplicates, near-duplicates with amount tolerance, and cost-code overlaps within a project period. An optional Claude review runs on the full budget weekly, reading the line scopes for narrative duplicates the rule engine cannot encode (the bath-fan duct that two different subs both described, the window casing that two different labor lines both included). Findings post to the validation queue with a recommended disposition, and the office manager approves, edits, or rejects each one before the next draw is finalized.