Overview
The Configurator solves the configure-to-order problem. Given a product family with many valid combinations of options — engine types, roof types, accessories, finishes — it walks through the option tree, makes selections, pins material attributes, and resolves those decisions into a concrete parts list for a specific unit.
The data model has three layers:
- Configuration Templates — the possibility space (every valid option, every valid combination, all nested sub-options, and the materials each option pulls in)
- Configurations — identity-only containers (a name and description) that group revisions for a single real-world order
- Revisions — immutable state-bearing snapshots under a configuration, carrying the actual selections, material assignments, and resolved parts list
The Configurator is agent-assisted by design. Dexter is the primary operator of the per-turn decision loop: it reads an order document (PDF, pasted text, ERP export), maps its contents onto the template’s option classes, proposes each decision one at a time via ask_question, and the user confirms before each write. The user can override any decision at any point.
Key Concepts
Configuration Template
The full option space for a configurable product. A template carries:
| Field | Notes |
|---|
entity_id | The output entity this template produces |
name | Display name for the configurable product |
description | Optional longer description |
revision_log | Freeform changelog — appended when the template is edited mid-run |
materials | Array of top-level materials always included regardless of selections |
option_classes | Array of top-level decision points |
Templates have two parallel sibling arrays — materials and option_classes — at every level of the tree. There is no single polymorphic components array.
Materials
A fixed part included when its parent option (or the template root) is selected. Materials carry:
| Field | Notes |
|---|
material_id | Required. Stable key that revision-level material assignments reference — must not change across template edits |
entity_id | Required. The entity slug for this part |
name | Human-readable label |
quantity | Fixed units (default 1). Quantity is not configurable — if a material can appear in different quantities, model them as separate materials with separate IDs |
instructions | Sourcing rules, decision criteria, or cross-class constraints — surfaced on the state script’s frontier at decision time so the agent sees them when proposing |
Part numbers do not live on template materials. They belong on revisions, via material assignment attribute values.
Option Classes and Options
An Option Class is a group of interchangeable choices — the core decision unit:
| Field | Notes |
|---|
option_class_id | Slug used as the key in selections (e.g., roof-type) |
name | User-facing label (e.g., Roof Type) |
instructions | Cross-class rules, sourcing guidance, or decision criteria — surfaced on the frontier at decision time |
selection_type | Constraint on choice cardinality (see below) |
options | Array of Option entries |
Selection types:
select_one — exactly one option must be chosen
select_many — zero or more options may be chosen (none is valid)
select_at_least_one — one or more options must be chosen (zero is invalid)
An Option is a single choice within an option class — option_id, name, and its own materials and option_classes arrays. That nesting is recursive: selecting “diesel engine” can reveal a nested Turbo Configuration option class that wasn’t reachable until the engine choice was made.
Configurations
A Configuration is an identity-only container — just a name and description. It groups revisions for a single real-world order or quote.
| Field | Notes |
|---|
name | Configuration name (e.g., Quote 58841 — Time Manufacturing) |
description | Optional description |
All state — selections, material assignments, resolved materials — lives on revisions, not on the configuration itself.
Revisions
A Revision is an immutable state-bearing snapshot under a configuration. Once written, it cannot be modified. The revision with the highest revision_number is the active state.
| Field | Notes |
|---|
slug | Stable identifier (matches the filename) |
revision_number | Integer, starts at 1, immutable post-create. Monotonically increasing per configuration |
revision_notes | What changed vs. the prior revision (empty for revision 1) |
selections | Array of option class selections |
material_assignments | Array of material attribute pinnings |
Each selection records:
| Field | Notes |
|---|
option_class_id | References the template’s option class |
chosen_option_ids | Array of selected option IDs (cardinality enforced by selection_type) |
notes | Freeform context about the decision |
Each material assignment records:
| Field | Notes |
|---|
material_id | Stable key from the template tree |
attribute_values | Array of VariantCondition entries pinning attribute values (see below) |
source | Where the data came from — an audit trail pointer (e.g., “Epicor part 87-A412”, “customer order PDF, line 3”) |
notes | Why the decision was made — reasoning, edge cases |
Variant Conditions
Material assignments pin attribute values using VariantCondition structures — the same system that BOM variant conditions use:
boolean — true or false
discrete_number — one or more specific numbers (e.g., [12, 24])
discrete_text — one or more specific text values (e.g., ["red", "blue"])
range — numeric range with inclusive/exclusive bounds
The Phase Model
The state script computes the current phase of a revision, which drives the entire workflow:
| Phase | Meaning |
|---|
pending | No decisions made yet |
selecting_options | Some option classes decided; the frontier carries the next decisions |
assigning_materials | All option classes decided; materials need attribute pinning |
completed | Everything decided, no errors, no stale entries |
The State Script
The state script is the single source of truth for where a configuration run stands. Given a template and a revision, it computes:
| Output | Meaning |
|---|
phase | Current phase (see above) |
next | The frontier — the list of decisions that need to be made next (each tagged as option_class or material, with a reference, name, and instructions) |
stale | Selections or material assignments whose keys are no longer reachable (e.g., because a parent option was changed) |
errors | Structural problems — unknown IDs, cardinality violations, unknown entity slugs |
resolved_materials | The flat parts list that follows from current selections |
The agent re-calls the state script at the start of every turn and after every write. The conversation transcript is not a substitute.
Creating a New Configuration
- Pick a Configuration Template
- Upload an order document (PDF, spec sheet, customer email) or paste relevant text
- The agent calls the state script to get the current phase and frontier
- Option selection phase — for each option class on the frontier:
- The agent identifies the next decision, reads the relevant order data, and proposes a selection
- The agent presents the decision to the user via
ask_question for confirmation
- On confirmation, the agent writes the selection to the revision
- The agent re-calls the state script — newly reachable nested option classes appear on the frontier
- The loop continues until all option classes are decided
- Material assignment phase — the agent pins attribute values on each reachable material. The cadence differs from option selection: when a single source document (an Epicor extract, an uploaded CSV, a customer order PDF) covers multiple materials, one
ask_question authorizes the entire batch and a single write lands them all. The per-material fallback only applies when a material’s value can’t be grounded in a shared source.
- The agent identifies materials on the frontier and groups them by source
- The agent presents assignments via
ask_question with source and notes for each
- On confirmation, the agent writes the material assignments
- The loop continues until all materials are assigned
- When the state script returns
phase: "completed", the revision is done
Every selection, every material assignment, and every template edit is gated by ask_question. Option selections are strictly one per round-trip. Material assignments can be source-batched — multiple materials pinned in one write when they share a grounded source.
Reconfiguring an Existing Configuration
When a change request lands against a saved configuration — updated order, customer email, engineering change notice — a new revision is created under the same configuration.
- Find the latest revision (highest
revision_number) and read its selections and material assignments
- Write a new revision file with:
- A descriptive slug (e.g.,
rev-3-hardtop-swap)
revision_number incremented by 1
revision_notes describing the delta (e.g., “Switched roof type to hardtop per customer request 2026-05-12”)
- All unchanged selections and material assignments carried forward
- Changed entries removed — these become open decisions again
- The per-turn loop re-walks the frontier for the removed decisions, just like an initial configuration
- Stale entries (selections or assignments that are no longer reachable due to the changes) are resolved
- When
phase: "completed", the new revision is done
The configuration row itself is untouched — reconfiguration only adds a new revision. The version chain is implicit in the revision numbering; there is no explicit parent pointer.
Carry forward everything the change doesn’t touch. Remove only the selections and assignments that the change cascades into, then let the per-turn loop handle the gaps.
Errors and Stale Entries
Before advancing the frontier, the agent resolves two surfaces the state script returns:
- Errors — structural problems that must be fixed: unknown option IDs, cardinality violations (e.g., two selections on a
select_one class), unknown entity slugs
- Stale entries — selections or material assignments whose keys are no longer reachable. This happens when a parent option changes and previously-selected nested options become orphaned
Both must be resolved before a revision can reach phase: "completed".
Templates
A template is the source of truth for what’s configurable. Building one well is the highest-leverage work in the Configurator — the option tree’s shape determines what a quote-builder can answer.
Templates are typically authored in two ways:
- From scratch — for genuinely new configurable products
- From an ERP / config table export — pipeline-mediated, using a pipeline to transform tabular data into the template JSON
When authoring templates:
material_id must be stable across template edits — revisions key off it. Changing a material ID breaks existing revisions’ references
- Part numbers belong on revisions, not templates. The template defines the material; the revision pins its attributes (including part number) via material assignments
- Every entity referenced from a template must declare a
part_number attribute with attr_type: text — this is an authoring prerequisite for the material assignment phase. Text typing is required because part numbers are alphanumeric (e.g., "87-A412"). A missing attribute slot means the material assignment phase can’t pin a part number to that material
instructions fields on option classes and materials carry cross-class rules, sourcing guidance, and decision criteria that the agent reads when making proposals
- Quantity is fixed at the template level. If a material can appear in different quantities depending on configuration, model them as separate materials with separate IDs
Mid-Run Template Edits
Templates can be edited during a configuration run — when a customer’s order doesn’t fit the current option set. The edit is gated by ask_question, the change is written to the template file, and a one-line entry is appended to the revision_log field. The per-turn loop then resumes with the updated template.
Relationship to Other Features
Standard BOMs
The most important distinction: standard BOMs are flat production recipes; Configuration Templates define an option space. BOMs feed directly into simulation and planning; templates do not — they produce revisions, which carry a per-order parts list.
If you find yourself wanting to use a template directly in a planning model, that’s the signal you actually need a BOM.
Entities
A template references entities in two places: the template root (the configurable product family) and the materials inside each option (the specific parts each selection pulls in). Entities also carry the attributes that the material assignment phase pins — the same attribute system that BOM variant conditions use.
Planning
A completed revision’s resolved materials list could in principle feed planning demand orders — if you know a specific order needs a specific parts list, that informs SKU demand. Today this linkage is manual or pipeline-mediated, not automatic. Completing a revision does not auto-create a demand order.
Simulation Models
Simulation models reference standard BOMs and entities; they do not consume Configuration Templates or revisions directly. To simulate a configurable product, build the corresponding BOM and ensure the model’s parameters reflect the configuration you care about.
Best Practices
Template Design
Shape the tree around the customer’s decision sequence, not the BOM structure. The option tree should mirror the order a customer or sales engineer actually thinks about choices — roof type before roof accessories, engine before turbo configuration. When the tree follows the natural decision order, the agent’s proposals align with what the order document says, and the user spends less time correcting sequence-dependent misreads.
Prefer shallow nesting with clear gates over deep nesting. Every nesting level adds a decision the user must confirm before the nested options become reachable. Two or three levels deep is normal; beyond that, ask whether the inner decisions genuinely depend on the outer one or whether they’re independent choices that happen to be grouped together. Independent choices belong at the same level, not nested.
Use select_one only when the choice is genuinely forced. If there’s a legitimate “none” case — an optional feature the customer might not want — select_many is the right cardinality, even if most customers pick exactly one. A select_one class with no valid “none” option forces the agent to pin an arbitrary default when the order document is silent on the topic.
Write instructions as if the agent has never seen this product before. The instructions field is the only guidance the agent reads at decision time beyond the option names themselves. Encode the decision criteria explicitly: “Choose aluminum when the customer specifies a corrosive environment; choose steel otherwise.” Don’t assume the agent will infer cross-class dependencies — if selecting a heavy chassis means the tow package must include a class-IV hitch, say so in instructions.
One material per attribute-varying part. If the same physical part can appear with different attribute values depending on the configuration (different voltages, different lengths), model them as separate materials with separate material_ids gated by the appropriate option. The material assignment phase pins attributes per material_id — a single material_id that needs different values in different configurations has no clean resolution.
Order Document Prep
Structured order documents produce faster, more accurate runs. When the order document cleanly maps to the template’s option classes — a line item per choice, a column per attribute — the agent can ground every proposal in a specific line or cell reference. Unstructured narratives (“customer wants the heavy-duty package with the upgraded electrical”) work, but they produce more proposals that need user verification.
Include part numbers in the order document or a companion file. Since part numbers live on revisions, not templates, they need to come from somewhere during the material assignment phase. An Epicor extract, a part-number mapping CSV, or explicit part numbers in the order document give the agent a grounded source to cite. When part numbers aren’t in the source data, the agent will ask for each one individually — which is correct but slow.
Flag what’s different about this order up front. If the customer is asking for something the template doesn’t currently support — a new option, an unusual quantity, a combination that doesn’t fit the tree — mention it at the start of the run. The agent can propose a mid-run template edit early rather than discovering the gap partway through the option selection phase.
Working with the Agent During a Run
Correct early, not late. If the agent’s proposal for an option class is wrong, override it immediately rather than confirming and planning to fix it later. Option selections can unlock nested option classes — a wrong selection at the top propagates through the tree, and correcting it later means a reconfiguration that re-walks every downstream decision.
Use the notes field. When confirming a selection or material assignment, adding context to the notes field (“customer verbally confirmed 24V on 2026-05-10”, “defaulting to standard length per engineering guidance”) pays off during audits and reconfiguration. The agent proposes notes based on what it knows, but user-supplied context is often more specific.
Let source-batching work for you during material assignments. When you have a single source document that covers many materials — a part-number mapping file, an Epicor extract — upload it and let the agent batch the assignments by source. This is significantly faster than pinning materials one at a time and produces a cleaner audit trail (every assignment in the batch cites the same source).
Reconfiguration Strategy
Name your revision slugs for the change, not the sequence. rev-3-hardtop-swap tells you what happened; rev-3 tells you nothing. When you’re scanning a directory of revisions six months later trying to understand the history of a customer order, descriptive slugs save real time.
Write revision_notes that describe the delta, not the final state. “Switched roof type from canopy to hardtop per customer email 2026-05-12” is useful. “Roof type: hardtop” requires the reader to diff against the prior revision to understand what changed.
Prune aggressively on reconfiguration. When carrying forward state from the prior revision, remove anything the change might cascade into — even if you think it’s probably still correct. The per-turn loop will surface the gaps, and the ask_question gate will bring the values back in with fresh verification. Carrying forward an assignment because it’s “probably still right” is how stale values survive into completed revisions.
Don’t reconfigure what you should re-create. If the change is so extensive that most selections and assignments need to change, a fresh configuration (new revision 1) against the same template is cleaner than a reconfiguration that removes almost everything and re-walks it. Rule of thumb: if you’re removing more than half the entries, start fresh.
General
Treat empty source fields as audit failures. A material assignment without a source is a value no one can trace. The state script won’t reject it, but the next person who reads the revision — or the next reconfiguration that needs to decide whether to carry it forward — has no way to evaluate whether the value is still correct.
Keep templates and revisions in sync on entity definitions. Every entity_id referenced in a template needs a corresponding entity definition with the right attributes (especially part_number with attr_type: text). Missing entity definitions or missing attributes don’t fail template authoring — they fail the material assignment phase, which is later and harder to debug.
Common Pitfalls
Configuration Templates are not BOMs. A template can’t be referenced from simulation or planning. If a feature you’re building needs to feed the optimizer, build a standard BOM.
Every decision is gated by ask_question. Option selections are one per round-trip; material assignments can be source-batched. The user confirms before anything is written. If you want to change a decision the agent proposed, say so during the confirmation step — once written to a revision, all decisions are immutable.
Nested option classes are invisible until parent selections are made. The state script’s frontier only includes option classes that are reachable given current selections. Nested classes that live inside a specific option only appear once that option is selected.
A few more to watch for:
select_many allows zero selections; select_at_least_one does not. Validation catches the difference, but the constraint is easy to misread when authoring a template.
- Revisions are immutable once written. To change a completed revision, reconfigure it — which creates a new revision with an incremented
revision_number. There’s no in-place edit.
- Empty
source on a material assignment is an audit-trail gap. The agent will back up and ask if it can’t ground the value in user-stated facts or source data.
revision_notes should describe the delta, not just the new state: “Switched roof type to hardtop per customer request” rather than “Hardtop selected.”
- Recursive option nesting is powerful but easy to overcomplicate. The state script catches structural errors, not poor design.
material_id must remain stable across template edits — it’s the key that revision-level assignments reference. Changing it orphans existing assignments.
Reference
- Template schema —
entity_id, name, description, revision_log, materials[], option_classes[] (parallel sibling arrays, recursive under each Option)
- Material schema —
material_id, entity_id, name, quantity, instructions
- Option Class schema —
option_class_id, name, instructions, selection_type, options[]
- Configuration schema —
name, description (identity-only container)
- Revision schema —
slug, revision_number, revision_notes, selections[], material_assignments[]
- Selection schema —
option_class_id, chosen_option_ids[], notes
- Material Assignment schema —
material_id, attribute_values[] (VariantCondition), source, notes
- VariantCondition types —
boolean, discrete_number, discrete_text, range
- Phase model —
pending, selecting_options, assigning_materials, completed
- State script output —
phase, next[], stale[], errors[], resolved_materials[]
- Selection types —
select_one, select_many, select_at_least_one