> ## Documentation Index
> Fetch the complete documentation index at: https://docs.truemath.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# A worked domain: contractor estimating

> Build a small contractor-estimating domain end to end — loaded labor rates, markup versus margin, a bid price that solves in several directions, and a cash-flow chart.

This page builds one small [domain](/concepts/domains) from start to finish, so you can see how the parts covered elsewhere in this section fit together. The subject is a general contractor pricing a job: a handful of [activities](/concepts/activities) connected only by the [variables](/concepts/variables) they share, answering the question every estimator starts from — *what should I bid?*

It is a focused starting point, not a full job-costing system. By the end it prices a job and charts the cash flow of the work; change orders, retainage, and progress billing are left as natural extensions. That is the point of a [library domain](/domain-library/overview): cover one workflow well and leave room to grow.

Throughout, one scenario keeps the numbers concrete: a worker at **$35 per hour**, **40 hours** of labor, **$8,000** of materials, a **\$32,000** subcontractor, **12%** overhead, and a target **20% margin**.

## From a wage to a loaded rate

The first [activity](/authoring/writing-activities) turns a base wage into a fully burdened rate:

```
loaded_labor_rate = base_wage * (1 + labor_burden_rate)
```

Writing this equation brings three variables into being. You then [configure each one](/authoring/defining-variables): `base_wage` and `loaded_labor_rate` are money — a `number` whose value carries a currency [unit](/math/units), so `35` is `35 USD` — and `labor_burden_rate` is a `percent`, entered as a decimal (`0.30` for 30%). At a $35 wage and 30% burden, the loaded rate is **$45.50\*\*.

The same equation answers more than one question. Mark all three variables [calculable](/authoring/writing-activities#calculable-variables) and the activity solves in whichever direction the inputs allow:

* *"What's my loaded rate at a \$35 wage and 30% burden?"* → solves `loaded_labor_rate`.
* *"What burden rate gives me a $52 loaded rate on a $40 wage?"* → solves `labor_burden_rate`.

You did not write a second equation for the reverse question. One relationship, marked calculable, covers both.

## Costing the work

A task's labor cost is the loaded rate times the hours:

```
labor_cost = loaded_labor_rate * labor_hours
```

`loaded_labor_rate` is the same variable the previous activity produces — naming it here is the whole of the wiring. That shared name is how the two activities connect, and why consistent keys matter. At $45.50 for 40 hours, labor is **$1,820\*\*.

For a crew, the natural input is a list of workers rather than a single rate. A [table](/authoring/tables) variable holds it — one row per worker, a loaded-rate column and an hours column — and one function totals it:

```
crew_cost = sumofproducts(crew_cost_table)
```

[`sumofproducts`](/math/functions/statistics#sum-of-products-sumofproducts) multiplies each row's two cells and sums the products: it is the right tool for any weighted sum. (Reaching for `sum` here would total only the first column — the rates — and ignore the hours.)

Materials and subcontractors follow the same markup shape:

```
material_sell_price = material_cost * (1 + material_markup_rate)
sub_sell_price      = sub_cost * (1 + sub_markup_rate)
```

At $8,000 of materials marked up 15% and a $32,000 subcontractor marked up 10%, those are **$9,200** and **$35,200**.

## Rolling up to a bid

The costed pieces sum into a direct cost, overhead is allocated on top, and the two give the job's total cost:

```
total_direct_cost = labor_cost + material_sell_price + sub_sell_price
overhead_amount   = total_direct_cost * overhead_rate
total_job_cost    = total_direct_cost + overhead_amount
```

For the scenario: direct cost **$46,220**, overhead at 12% **$5,546.40**, total job cost **\$51,766.40**. None of these activities needed explicit wiring — each names variables the earlier activities produce, and the domain composes into a single solvable chain. See [how activities compose](/authoring/writing-activities#how-activities-compose).

A contractor states the markup two ways, so there are two routes to the bid:

```
bid_price = total_job_cost / (1 - margin_pct)    # from a target margin
bid_price = total_job_cost * (1 + markup_pct)    # from a markup on cost
```

These are two activities that produce the **same** variable from different inputs. Supply a margin and the first resolves; supply a markup and the second does. They are complementary pathways, not duplicates — the domain answers the question whichever way the estimator happens to phrase it. A 20% margin gives a bid of **\$64,708**.

## Margin is not markup

`margin_pct` and `markup_pct` are the kind of pair that quietly breaks a domain if you let them blur together. Margin is a percentage of the **sell price**; markup is a percentage added to the **cost**. A 20% margin is a 25% markup on the same job — the bid is identical, the number is not.

Two small activities convert between them:

```
markup_pct = margin_pct / (1 - margin_pct)
margin_pct = markup_pct / (1 + markup_pct)
```

The real safeguard is in the [descriptions](/authoring/defining-variables). Because a contractor might say "twenty percent" for either, each variable's description claims its own phrasing and names the other to rule it out — *margin is a percentage of the bid price, distinct from markup, which is a percentage added to cost.* That mutual disambiguation is how you keep results landing on the variable you mean.

<Tip>
  When two variables compete for the same words, resolve it in the descriptions, not the equations — and make the distinction point both ways, so each names the other. A pair separated in only one description still collides from the other side.
</Tip>

## Seeing the result

Pricing the job is one workflow; tracking its cash is the next, and it shows how a domain turns a series of values into a picture. Say the work bills and pays out over several months. Two [table](/authoring/tables) inputs hold the draws and the costs, one row per period, and a subtraction gives the net per period:

```
net_cash_per_period = draw_schedule - cost_schedule
```

Two tables of the same shape subtract element-wise, so this is a one-row-per-period table of net cash — positive in months you collect more than you spend, negative when you spend ahead of billing. Give that variable a `bar_chart` [display type](/authoring/charts) and it reads as bars above and below zero; the chart is a presentation of the table, not a separate value.

The running total — *how far underwater is the job at its worst?* — is a [loop](/math/functions/conditionals-and-logic#building-a-series) that carries a cumulative sum:

```
cumulative_cash_position = loop(1; length(net_cash_per_period)) + (last + item(net_cash_per_period; index))
```

Each row adds the period's net cash to the running total in `last`, so the table climbs and dips with the project and its final row is the job's net cash position. The functions here — [`loop`](/math/functions/conditionals-and-logic#loop-loop), [`length`](/math/functions/tables#length-length), [`item`](/math/functions/tables#item-item) — are covered in the function library.

## Where it could go next

The domain prices a job and shows its cash flow — a real tool an estimator would use, and an obvious invitation to extend:

* *"Add a change order at a price and roll it into the contract value."*
* *"Withhold retainage from each draw and track the balance."*
* *"Track cost-to-date against the budget and project the cost at completion."*

Each is a few more activities over the variables already here. Before publishing any of it, round-trip the calculable variables in the Test view to catch [drift](/authoring/testing-and-publishing#testing-a-draft): solve `bid_price` from a margin, then recover that margin from the bid, and confirm the value holds. Because the activities are reversible, it should.
