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

# Configuration Templates

> Author the possibility space for a configurable product: option classes, materials that reference entities, and nested decisions.

## Overview

A **configuration template** captures the possibility space for a configurable product — the option tree and the entity references a configurator session walks through. Materials don't live in a separate library; each one references an entity in the shared [entity registry](/reference/entities). A template is authored once per product line and reused across every customer order the line serves. A concrete walk through the template is saved as a **revision** under a [Configuration](/product/configurations) — the Configuration is the container for one customer order, and each revision is an immutable snapshot of the selections and material assignments made against the template. The [Configurator](/product/configurator) drives the walk.

Templates live in the templates list on the **Configurator** page; create one with the **New template** action.

<Warning>
  Templates are **not [BOMs](/product/bom)**. Planning and simulation do not read templates directly — they read BOMs and entities. A template describes what a product *can* be; the BOM describes how a specific thing *is* made.
</Warning>

<Info>
  "Template" is an overloaded word. A **configuration template** (this page) is not a data [pipeline](/product/templates) and not a report template — three unrelated features happen to share the name. This page is only about the option tree a Configurator walks.
</Info>

## Structure

Every template is a tree with **two typed sibling lists at every level** — `materials` (parts that are always included) and `option_classes` (decision points that branch). There is no polymorphic components array; a material and an option class are distinct kinds of nodes.

Top-level fields:

| Field              | Notes                                                                |
| ------------------ | -------------------------------------------------------------------- |
| `name`             | Display name for the template (1–255 characters)                     |
| `description`      | Optional; defaults to empty                                          |
| `entity_id`        | The entity this template produces                                    |
| `materials[]`      | Always included regardless of selections                             |
| `option_classes[]` | Top-level decision points                                            |
| `revision_log`     | Freeform changelog appended when the template is edited during a run |

### Materials

| Field          | Notes                                                                                                              |
| -------------- | ------------------------------------------------------------------------------------------------------------------ |
| `material_id`  | Stable key that revisions pin to. Must not change across template edits.                                           |
| `entity_id`    | References an entity in the shared registry                                                                        |
| `name`         | Display name. Optional; defaults to empty.                                                                         |
| `quantity`     | Integer ≥ 1, default 1. Fixed at template level — different quantities are different materials with different IDs. |
| `instructions` | Freeform guidance read at decision time when this material's attributes are pinned                                 |

### Option Classes

| Field             | Notes                                                                                         |
| ----------------- | --------------------------------------------------------------------------------------------- |
| `option_class_id` | Short slug like `roof-type`                                                                   |
| `name`            | User-facing label                                                                             |
| `selection_type`  | `select_one` (exactly one), `select_many` (zero or more), `select_at_least_one` (one or more) |
| `options[]`       | The choices within the class                                                                  |
| `instructions`    | Freeform — including cross-class rules the workflow does not enforce automatically            |

### Options

Each option carries `option_id`, `name`, its own `materials[]`, and its own `option_classes[]`. Nesting is **recursive**: picking an option can expose nested option classes that were invisible until then — choosing `diesel-engine` reveals a `turbo-configuration` class beneath it. Nested classes only become reachable once their containing option is selected.

### The `instructions` Field

`instructions` on materials and option classes is prose read at decision time. Use it for:

* **Cross-tree rules** — *"If `chassis-size: heavy` is selected, the `tow-package` class must include `class-iv-hitch`."*
* **Sourcing rules** — *"Pin `voltage` from the customer's electrical spec; default to the entity's standard voltage if absent."*
* **Decision criteria** — *"Choose `aluminum` for corrosive environments; steel otherwise."*

<Warning>
  The Configurator does not enforce cross-class rules automatically — the prose is the contract. Dexter reads `instructions` alongside each decision, and the operator confirms. This is a stopgap until a declarative constraint DSL lands; for now, prose plus confirmation is how cross-class rules are honored.
</Warning>

### The `revision_log` Field

`revision_log` is a chronological changelog. A one-line entry is appended when a structural change is made to an existing template mid-run — splitting a static material into a `select_one` class to accommodate a new quantity, or adding an option because a customer order didn't fit. It stays empty when authoring the initial template.

## Creating a Template

Two paths:

### From scratch

Best for a new product line where you already know the decision shape.

1. Create the entities the template will reference. Every entity that appears on a material must exist first, and each must carry a `part_number` attribute (see prerequisites below).
2. Create the template with a name, description, and the `entity_id` of the finished good.
3. Add top-level materials for everything that's always included.
4. Add top-level option classes for the first-level decisions.
5. Under each option, add nested materials and nested option classes as the decision tree requires.
6. Fill `instructions` on any class or material with non-obvious sourcing rules, cross-tree constraints, or decision criteria.
7. Save. Leave `revision_log` empty.

### From an ERP or config-table export via pipeline

Best when the shape of the product already lives in an ERP config table or a spreadsheet. Ingest the source through a [pipeline](/product/templates) whose output is the template:

1. Point the pipeline at the config table (or upload it).
2. Map the description column to each material's `name`, and derive its `entity_id` slug from the same description — not from the part-number column.
3. Map the quantity column to `quantity`.
4. Do **not** map part numbers into the template. Carry them separately so they can be pinned per revision when the Configurator runs.
5. Review the generated template — option-class structure and `instructions` almost always need a manual pass, because source tables rarely encode decision semantics.

<Info>
  Recording the derivation as a pipeline (rather than a one-shot import) lets the template retrace to its source when the ERP config changes.
</Info>

## Editing a Template

Templates are editable throughout their life, in two contexts:

* **Between runs** — edit freely. Existing configurations reference materials by `material_id`, so don't rename those keys.
* **Mid-run** — allowed when a customer order doesn't fit the current option set. The [Configurator](/product/configurator) gates the change through an ask-confirm turn; on approval the template is updated, a one-line entry is appended to `revision_log`, and the run resumes against the updated template.

<Note>
  Mid-run edits change the template **globally** — every other configuration against the same template sees the update on its next state-script call.
</Note>

## Authoring Rules and Prerequisites

* **`material_id` is stable.** Revisions pin to it; renaming a `material_id` breaks the addressing key for every prior revision that referenced it. Add a new material and phase the old one out instead.
* **A `part_number` attribute is required on every referenced entity**, declared with `attr_type: text`. Text typing accommodates alphanumeric numbers (like `PN-1001-A`) and lets a not-yet-numbered part be pinned to a placeholder value until the real number lands. If the attribute is missing when the Configurator reaches the material assignment phase, add it to the entity definition first.
* **Part numbers do not live on template materials.** They belong on [revisions](/product/configurations) as material-assignment attribute values.
* **`quantity` is fixed at the template level.** If the same part appears in two contexts with different quantities, model two separate materials with different IDs. If quantity itself is a customer decision, model it as a `select_one` option class where each option carries a differently-quantified material.
* **Entities before templates.** A template's material references resolve at write time; every entity must already exist.
* **Slugs are unique per factory.**

## Limits and Caveats

* Cross-class rules in `instructions` are not machine-enforced; selections that violate them are not rejected automatically. Declarative constraints are a planned capability; until they land, prose is the only mechanism.
* Deep option trees (4+ levels) are supported but hard to walk and hard to reconfigure — keep nesting shallow when the product allows it.
* `option_id` must be unique within its own option class; `option_class_id` must be unique across the whole template, since selections address classes by ID. A class in one option can technically share an ID with a class in a different option, but conflating them makes the audit trail hard to read.
* Renaming `material_id` after any configuration has been built is the single most common way to break existing revisions.
