Overview
Every time you change a model, ProDex runs a validation pass that checks the model against a comprehensive set of correctness rules. The result is the play button at the bottom of the canvas: green means the model is valid and ready to run; red means there’s something blocking simulation. Validation happens continuously while you build, which means you catch problems as you introduce them rather than discovering them hours later when a simulation fails.The Red Play Button
When the play button at the bottom of the canvas is green, hovering it shows the tooltip “Run simulation.” Click it and the simulation runs. When it’s red, hovering opens a popover with:- A header counting the problems (e.g., “1 validation error” or “6 validation errors”).
- The verbatim error text from the validator, naming the offending component and what’s wrong with it.
- A Fix with Assistant button (with a sparkles icon) that hands the error to Dexter.
“Component ‘Halo Repress Halo Queue’ (type: BufferDefinition) must have exactly one outgoing standard connection, but has 2.”Clicking Fix with Assistant drops that exact error into a Dexter conversation and asks it to propose a corrective change. Dexter reads the error, locates the component on the canvas, and suggests the edit — usually deleting the extra connection or splitting the buffer’s downstream into a Router. You review the proposed change before it’s applied. If you’d rather fix it yourself, the error message is specific enough to point you at the field, component, or expression to edit. The popover dismisses on un-hover, so reopen it if you need to re-read.
What Gets Validated
The validator runs about seventeen distinct rule categories. The list below covers the most consequential ones — the ones that block real models day-to-day.Configuration
Required top-level fields, file-format correctness, schedule presence (when required). A model without ametadata.json or with malformed JSON fails before any rule below it runs.
Naming and Identifiers
- Component
ids must be unique across all resources, stations, and components in the model ::is reserved as the path separator — no single component id may contain it- State variable names must not collide with components, entities, constants, lookup tables, or reserved DSL identifiers
- Topic names must be unique
- Entity attribute names must be unique per entity type
Connection Topology
Validation enforces strict cardinality on connections:- Source — 0 incoming, exactly 1 outgoing
- Sink — 0 outgoing
- Router, ModelNode — at least 1 outgoing
- Process, Buffer, Combiner, Separator, Transformer — exactly 1 outgoing standard connection
- You cannot draw a connection to or from a Station. Stations hold components; connections wire components, not containers.
Entity Flow Consistency
Each component declares which entity types it accepts and emits. Validation checks that:- Every component’s expected input types are produced somewhere upstream
- No component receives a type it can’t handle (the flow doesn’t carry a
raw_materialinto a process expectingpainted_part) - Combiner and Separator type expectations match the surrounding flow
Entities
Attribute names unique per entity type;choices arrays valid for list types; lower_bound ≤ upper_bound on Number attributes; length non-negative on Text.
Components and Stations
Required fields per component type (e.g.,station_id on Process, Combiner, Separator, and Transformer — even though the schema defaults it to null, the validator rejects models that leave it unset). Station capacity keys must match entity types that enter the station; including an internally-created output type fails.
Resources
Every defined resource must be referenced by at least one component; every component reference must point at a real resource.Routers
Probabilistic routes have non-negative weights; conditional routes have non-empty rule lists; type-based routes cover the expected input types.Transformers
Output entity types declared on the Transformer match the actual emitted types.Entity-Generator Attribute Assignment
The strictest set of attribute-type restrictions:dsl_expris not valid fortext_listornumber_listattributesround_robinandrandom_choiceare valid only forboolean,text_list, andnumber_listrandom(probability distribution sampling) is valid only fornumberfixedandweightedare valid for any type
DSL Expressions
The DSL is statically typed. Every expression is checked at validation time, not at runtime:- Boolean fields (conditions, gates) must produce booleans
- Numeric fields must produce numbers
- An aggregation function (
SUM,MAX,ANY, etc.) outside a multi-entity context fails - An identifier not available in the expression’s context (e.g., an entity attribute in a no-entity context) fails
- A referenced constant, lookup table, or state variable that doesn’t exist in the model’s opt-in list fails
State Variables
Each state variable has atype (one of number, text, boolean) and a required initial_value matching that type. Mismatched types fail.
Topics
Topic names unique; the topic-trigger graph (edges from topic A to topic B exist when a listener on A emits B) must be acyclic. Topic cycles are caught here before they can cause runaway behavior at simulation time.Event Hooks and Listeners
Hooks reference valid lifecycle events for their host component type —process started on a Process is fine, process started on a Resource fails. Listener topic fields reference declared topics. Per-component action restrictions are enforced (see Event System). Two specific direct self-loops are forbidden: a Source’s on_entity_created hook running a release action, and a Buffer’s on_entity_exited hook running a release action — both fail validation.
Flow-graph cycles are explicitly allowed. Routing entities back through an upstream Process (rework loops) is a valid pattern. Only event-driven loops (topic acyclicity, direct self-loops) are forbidden.
Constants and Lookup Tables
Multi-key lookup tables require allLOOKUP() calls to pass the right number of keys with matching types. Key possible_keys allowlists (when declared) are enforced. Each model’s constants[] / lookup_tables[] opt-in arrays must reference real factory-level slugs.
Schedule
- All datetime fields (Start Time, End Time,
release_time) must be timezone-aware ISO datetimes. Naive datetimes fail validation immediately — no implicit timezone fallback. - Every event timestamp must fall between Start Time and End Time (or Start Time + model Duration if End Time isn’t set)
element_idreferences must resolve to real components (including hierarchicalparent::childpaths)- Material releases target Sources or Buffers; releases to other component types fail
- Scheduled actions are restricted to
assignandemit— component-bound actions (pause,resume,set capacity,release) are not valid in schedules. Use a scheduledemitto a topic and let an Event Listener handle the side effect.
Nested Models (Model Nodes)
Validation recurses into every nested model referenced by a ModelNode. Errors inside a sub-model bubble up with a path prefix —"Nested model 'cell_a' (used by model node 'fab_cell'): {error}". ModelNode nested_model_id must appear in the parent’s imported_models[] list. Mapping fields (input_mappings, output_mappings, resource_mappings, topic_mappings, state_variable_mappings) all resolve against real targets.
Simulation Duration
Maximum simulation duration is 365 days. The schema doesn’t enforce this — it’s a validation-layer rule. Models with longer durations fail capture even though the JSON itself is well-formed.Sources Without Arrival Logic
A Source isn’t required to have anarrival_logic field. Three independent arrival modes can coexist on the same Source: stochastic arrivals from arrival_logic, scheduled releases from a schedule’s material_releases[], and event-driven releases from release actions on Event Hooks or Listeners.
A Source with no arrival_logic, no schedule material releases, and no release actions has no way to produce entities — that’s a no-op model, not a validation error. The validator allows it because it’s a legitimate intermediate state during authoring.
What Validation Does NOT Catch
Validation catches structural and type-level correctness. A few classes of issue slip through:- Semantic correctness. A model that compiles and runs may still encode wrong assumptions about your operation. Validation can’t tell you a 30-second process should really be 30 minutes.
- Resource starvation patterns. A model where a critical resource is held forever by a stuck entity validates fine but produces meaningless results. Read the run’s results to catch these.
- Sensible Monte Carlo / experiment design. Validation runs on a single model; it doesn’t reason about whether your sweep of snapshots is statistically informative.
- Performance. A valid model can still take a long time to run. Use Snapshots and short Durations to iterate fast.
Validation and Dexter
Validation applies to changes Dexter makes, too. When Dexter modifies your model, the same rules run. If Dexter tries to configure a component that would fail validation, the platform rejects the change and Dexter has to retry or ask you to clarify. This is one of the mechanisms that keeps Dexter from silently corrupting a working model. If Dexter is repeatedly failing to apply a change, check the validation errors yourself — often the fix is a small correction Dexter couldn’t infer (a missing constant, an attribute name that doesn’t exist on the entity type) and telling Dexter the specific fact unblocks it. The Fix with Assistant button is the same channel in reverse: it gives Dexter the exact error text so it doesn’t have to guess.Tips
- Fix structural errors first. Disconnected-component errors often hide expression errors that can’t run until the structure is sound.
- The error messages are specific. If an error is confusing, search the message text in the docs — most validation errors map directly to concepts covered in the reference.
- Validation is fast. You can make sweeping edits and let validation check them — you don’t need to be surgical.
- Red play button is your friend. It’s a forcing function that prevents you from wasting time on a simulation that was going to fail. Treat each red state as information, not an obstacle.

