1. Purpose

A Recipe is a reusable, named combination of Resources — with its own Worksheet, logic, at least one Input Parameter, and a declared Output Unit. Recipes live in a shared Recipe Library accessible to all Estimates in a workspace. They model reusable methodologies or “crew compositions” that can be dropped into Item Worksheets and instantiated with different input values.

Everything in a Recipe is structured the same way as an Item’s Worksheet:

  • Variables and Calculation Blocks for internal logic
  • Worksheet Resources (Resource references with snapshot rates)
  • Nested Recipes (recursive composition)
  • Content Blocks (Inclusions, Exclusions, Task Breakdown, Risks)

The Recipe’s Worksheet evaluates to a rate per Output Unit. When dropped into an Item’s Worksheet, the host Item supplies a quantity (in the Output Unit) and the contribution = Recipe rate × quantity.

Analogy (glossary cooking model): the Recipe is the recipe — the reusable instructions (combination of ingredients and methodology) that produce a standardized output. Compare to Item (the cake, the final product) and Resource (the ingredient).


2. Attributes

AttributeTypeRequiredDefaultNotes
idUUIDgeneratedSystem-managed
namestring (short text)Human-readable name. Unique workspace-wide (no namespacing by Categorization)
descriptionstring (long text)nullWhat this Recipe represents and when to use it
output_unitUnit refThe Unit the Recipe’s result is expressed in (e.g., day, , pump). Required per BR-030
categorization_optionsmany-to-many[]Multi-select tags for library organisation (e.g., “Concrete Works”, “Labour”). Optional; never affects behaviour (BR-120)
noteslong textnullAdditional context; free form
created_at / updated_attimestampsystemAudit
created_by / updated_byUser refsystemAudit

Derived attributes (computed, not stored) — see §9.


3. Input Parameters

Definition: Named variables defined at the Recipe level (not at usage time). Every Recipe must declare ≥1 Input Parameter per BR-030.

Input Parameters are the inputs to the Recipe’s Worksheet — they drive the internal calculation. They are NOT the same as the Output Unit (which declares how the result is expressed). They are also NOT the same as Worksheet Variables (which are derived inside the Worksheet).

AttributeTypeRequiredNotes
namestringE.g., “Concrete Quantity”, “Number of Trips”, “Pile Depth”
unitUnit refThe unit this parameter is measured in
default_valuenumberOptional sensible default; used if host Worksheet doesn’t override
descriptionstringPlain English explanation of what this parameter represents

Semantics:

  • Input Parameters are required per Recipe (at least one). Each one must have a name and unit.
  • When a Worksheet Recipe (a usage of this Recipe in an Item Worksheet) is created, the host estimator provides values for each Input Parameter. These values can be constants or expressions referencing Worksheet Variables.
  • Input Parameters are available as Variables inside the Recipe’s Worksheet — the Recipe’s Calculation Blocks and Worksheet Resources reference them by name.

4. Output Unit Semantics

The Output Unit (a required Unit) declares the unit of measurement for the Recipe’s computed result.

How it works:

  1. The Recipe’s Worksheet evaluates all its Worksheet Resources, Calculation Blocks, and nested Recipes, producing a total cost.
  2. That total cost is divided by the (implicit or explicit) quantity per Output Unit to produce a rate per Output Unit.
  3. When a Worksheet Recipe references this Recipe, the host Worksheet multiplies the Recipe’s rate by a quantity (in Output Units) to get the total contribution.

Example — “Concrete Pump” Recipe:

  • Input Parameters: Concrete Quantity (m³), Number of Trips
  • Worksheet Resources: Pump mobilisation ($1,500), daily running ($800), demobilisation ($1,500), driver labour ($150/day)
  • Calculation: total cost based on inputs
  • Output Unit: day (the recipe is priced per day of pump hire)
  • Result: e.g., $1,200/day
  • When used: host Item provides quantity=3 (days) → contribution = $1,200 × 3 = $3,600

The Output Unit is a fixed property of the Recipe definition, not something the host Worksheet negotiates. This ensures the Recipe’s internal logic remains consistent wherever it’s used.


5. Lifecycle States

Alpha (v1) Recipes have no formal state machine. All Recipes are treated as Published — they can be used in Estimates immediately upon creation. Versioning concerns (e.g., “Recipe v1.0 vs v2.0”) are deferred to Beta (see Open items §11).

Rationale: In v1, a Recipe either exists (usable) or doesn’t. Divergence between the Recipe definition and a Worksheet Recipe that references it is flagged by Anomaly Review (BR-041/042/043), not prevented by state rules.

Future (Beta): Recipes may gain versioning — allowing a Recipe to evolve while Worksheet Recipes retain “snapshots” of older versions. Recipe snapshot semantics are governed by BR-041 (snapshot at embed), BR-042 (no auto-sync), BR-043 (divergence flagged by Anomaly Review).


6. Relationships

Inbound (things referring to Recipe)

FromCardinalityNotes
Worksheet RecipeWRec M:1 RecipeA Recipe can be dropped into many Item Worksheets; each usage is a Worksheet Recipe
Recipe (self, nested)Recipe 1:M RecipeA Recipe can contain nested Recipes via Worksheet Recipe (composition)

Outbound (things Recipe references)

ToCardinalityRequiredNotes
WorksheetRecipe 1:1 WorksheetEvery Recipe has exactly one (BR-006). Contains the Recipe’s methodology
Unit (Output Unit)Recipe M:1 UnitRequired; declares how the Recipe’s result is expressed
Input ParameterRecipe 1:M InputParameterRequired; ≥1 per Recipe (BR-030)
Categorization OptionRecipe M:M CatOptionOptional multi-select tags for library organisation
UserRecipe M:0..1 UserNot currently used; reserved for future ownership/lock

7. Validation / Invariants

Rules that must hold at all times:

  1. At least one Input Parameter. Every Recipe must have ≥1 Input Parameter defined. Enforced on create and on deletion (cannot delete the last one).
  2. Output Unit mandatory. output_unit cannot be null. Must reference a valid Unit from the library.
  3. Unique name within workspace. Recipe names must be unique across the entire Recipe Library. Open (§11): should uniqueness be per-Categorization instead (allowing “Concrete Pump” under both “Concrete Works” and “Formwork”)?
  4. Worksheet 1:1. Every Recipe has a Worksheet, created atomically when the Recipe is created. No orphaned Worksheets (cascade delete).
  5. Input Parameter unit validity. Each Input Parameter must reference a valid Unit from the library.
  6. Worksheet Resources in Recipe reference valid Resources. All Worksheet Resource references inside the Recipe’s Worksheet must point to Resources that exist in a Price Book (optionally across any Price Book in the workspace, or Estimate-scoped — open).
  7. No circular nesting. A Recipe cannot transitively include itself (via nested Recipes in the Worksheet). Validated on Worksheet Recipe creation.
  8. Categorization validity. All Categorization Options applied to a Recipe must belong to Categorizations whose scope includes Recipe (or “combination” scopes).

8. Derived / Computed Attributes

AttributeDerivationNotes
unit_costComputed by evaluating the Recipe’s Worksheet (sum of Worksheet Resources evaluated)Not stored; computed on demand when Worksheet Recipe supplies Input Parameter values
rate_per_output_unitunit_cost / quantity_per_output_unitWhere quantity_per_output_unit is implicit (1) unless the Worksheet explicitly scales by an Input Parameter
total_cost_when_instantiatedrate_per_output_unit × quantity_supplied_by_hostOnly computed when Worksheet Recipe is evaluated in the context of a host Item’s Worksheet
has_anomaliesTrue if any Anomaly Review check fires (recipe snapshot divergence, missing resources, etc.)UI badge

9. Worked Examples

Example A — “2-Person Concrete Crew” Recipe

A simple, reusable crew composition:

Recipe:
  name: "2-Person Concrete Crew"
  description: "Standard two-person team for concrete finishing. Includes labour + small tools markup."
  output_unit: hr (hour)
  input_parameters:
    - name: "hours"
      unit: hr
      default_value: null
      description: "Total labour hours for the crew"

Worksheet (1 Worksheet Resource):
  WR1: Resource "Finisher (skilled)" @ $65/hr, qty = hours × 1
  WR2: Resource "Finisher (semi-skilled)" @ $48/hr, qty = hours × 1
  WR3: Resource "Small tools markup" @ $8/hr, qty = hours × 1

Calculation Block:
  crew_cost_per_hour = 65 + 48 + 8 = $121/hr

Result: $121/hr

Example instantiation in an Item's Worksheet:
  Worksheet Recipe: "2-Person Concrete Crew"
    Input Parameter "hours" = 40 (hours)
  → Contribution = $121/hr × 40 hr = $4,840

Example B — “Concrete Pump” Recipe

A more complex recipe with multiple input parameters:

Recipe:
  name: "Concrete Pump (Boom Pump)"
  description: "Mobilisation, running, and demobilisation of a boom pump for concrete placement."
  output_unit: day
  input_parameters:
    - name: "Concrete Quantity"
      unit: m³
      default_value: null
      description: "Total cubic metres of concrete to be pumped"
    - name: "Number of Trips"
      unit: no (number)
      default_value: null
      description: "Number of separate pump-down operations (e.g., floor pours)"

Worksheet Variables:
  mob_cost = 1500 (fixed mobilisation, $/pump)
  demob_cost = 1500 (fixed demobilisation, $/pump)
  daily_rate = 800 ($/day operating cost)
  driver_rate = 150 ($/day driver labour)
  pump_efficiency = Concrete Quantity / Number of Trips (m³ per trip — informational)

Worksheet Resources:
  WR1: Resource "Pump Mobilisation (1 day)" @ $1,500/day, qty = 1
  WR2: Resource "Pump Daily Rate" @ $800/day, qty = 1
  WR3: Resource "Pump Driver" @ $150/day, qty = 1

Calculation Block:
  total_cost = mob_cost + demob_cost + daily_rate + driver_rate = $3,950

Result: $3,950/day

Example instantiation:
  Worksheet Recipe: "Concrete Pump"
    Input Param "Concrete Quantity" = 200 (m³)
    Input Param "Number of Trips" = 2 (trips)
  → Contribution = $3,950/day × 3 days = $11,850

Example C — “Pile Drive” Recipe (discrete output unit)

Demonstrates a Recipe with a count-based Output Unit:

Recipe:
  name: "Pile Drive (All-in)"
  description: "Complete pile driving service including mobilisation, setup, and demob."
  output_unit: pile (count — each pile)
  input_parameters:
    - name: "Pile Depth"
      unit: m (metres)
      default_value: null
      description: "Average depth of piles in the project"
    - name: "Number of Piles"
      unit: no
      default_value: null
      description: "Total number of piles to be driven"
    - name: "Pile Diameter"
      unit: mm
      default_value: 400
      description: "Diameter of pile shafts"

Worksheet Variables:
  mob_cost_per_project = 8000
  hourly_drive_rate = 50 (m/hour for a hammer rig)
  total_drive_hours = (Pile Depth × Number of Piles) / hourly_drive_rate
  drive_labour_cost_per_hour = 200

Worksheet Resources:
  WR1: Resource "Pile Rig (12-tonne hammer)" @ $1,200/day, qty = (total_drive_hours / 8) [days]
  WR2: Resource "Rig Driver" @ $200/day, qty = (total_drive_hours / 8) [days]
  WR3: Resource "Pile Mobilisation (all-in)" @ $8,000/project, qty = 1

Calculation Block:
  total_cost = (sum of WR costs) + pile-specific allowance per pile

Result: $X per pile

Example instantiation:
  Worksheet Recipe: "Pile Drive"
    Input Param "Pile Depth" = 18 (metres)
    Input Param "Number of Piles" = 24 (piles)
    Input Param "Pile Diameter" = 600 (mm)
  → Rate computed for all 24 piles
  → Contribution = rate_per_pile × 24

Example D — Recipe Promoted from an Item

Workflow showing promotion:

Initial state: Item "Concrete pier cap assembly" in Estimate A
  Worksheet contains:
    WR1: "Concrete 32MPa" @ $360/m³, qty = 1.5 m³
    WR2: "Reinforcement" @ $2,400/tonne, qty = 0.09 tonne
    WR3: "Formwork (hire)" @ $12/m², qty = 8 m²
    WR4: "Placement crew (3 days)" @ $900/day, qty = 3

Estimator decides: "We'll use this crew composition across many projects."

Action: Estimator opens Item's Worksheet and selects "Promote to Recipe" from menu.

Wizard flow:
  1. Name the Recipe: "Concrete Pier Cap Assembly (1.5m³, 6m² forming)"
  2. Define Output Unit: select "pier cap" (or "LS" if count isn't preferred; could be m³)
  3. Extract Input Parameters:
     - "Pier Cap Quantity" (qty of 1.5 m³ per cap → becomes input parameter)
     - "Forming Area per Cap" (m² per cap → becomes input parameter)
     - "Labour Days" (fixed at 3 days? Or parameterised? Estimator decides)
  4. Review Worksheet: auto-populated from Item's Worksheet
  5. Save as Recipe

Result: New Recipe in Library, ready to be dropped into Estimates B, C, D, etc.
  When used: different projects parameterise with their own Quantity and Forming Area