Compilation (Authoring)

The HIR pipeline turns user-authored TypeScript into checked, source-spanned buffer programs. Scenario code retains a separate same-realm compiler hardened against accidents, not attackers.

Inputs
Code strings stored on the SDCPN document (lambda, kernel, dynamics, metric, scenario)
Outputs
Versioned HIR artifacts for runtime code; InitialMarking for scenarios
Errors
Thrown at compile time with the offending item’s ID (SDCPNItemError) → surfaced as protocol error messages / result objects
Runs on
LSP worker (HIR compilation) · simulation workers (buffer programs) · main thread (scenarios and timeline metric evaluation)

The compilation pipeline (module-style code)

Dynamics, lambdas, kernels, and metrics all compile through the HIR. The first three are authored as export default Wrapper(fn) modules; metrics are authored as function bodies.

User source export default Lambda((input, parameters) => …) (TypeScript allowed)
Lower to HIR Parse the supported TypeScript subset with exact source spans
Check + analyze Validate against places, token layouts, parameters, and output shapes
Emit artifact Generate buffer-native JS with offsets and strides baked in
Instantiate in runtime Validate v4 fingerprint, then bind parameters, pool, and ABI views
The LSP virtual files (lsp/generate-virtual-files.ts) are the type-level twin of this pipeline: they generate the Color_*, Parameters, Dynamics, TransitionKernel… declarations that Monaco checks against, so editor diagnostics and runtime artifacts are produced from the same HIR checks and emitters.

What gets compiled, where it runs

Surface Authored as Compiled by Executed in Signature at runtime
Transition lambda module, Lambda(fn) compileHirArtifacts in the LSP worker Worker, per enablement check (f64, u64, u8, placeBases, indices) → boolean | rate
Transition kernel module, TransitionKernel(fn) compileHirArtifacts in the LSP worker Worker, per firing (views, placeBases, indices, outputViews, sink) → void
Dynamics module, Dynamics(fn) compileHirArtifacts (colours with a real element) Worker, per step per place (placeBytes, tokenCount) → Float64Array
Metric plain function body (no module) compileHirArtifacts in the LSP worker Main thread (timeline) · MC worker (experiments) (f64, u64, u8, placeCounts, placeOffsets) → finite number — NaN/∞ throw
Scenario per-place expressions or one code-mode body authoring/scenario/compile-scenario.ts Main thread, before createSimulation InitialMarking (+ scenario parameter values)
Place visualizer module, Visualization(fn) returning JSX Host UI package (@hashintel/petrinaut, ui/lib/compile-visualizer.ts — the one inherently React-based surface, since visualizers author JSX) Main thread render ({tokens, parameters}) → ReactElement

Scenario compilation → InitialMarking

per_place mode

Uncoloured place → expression string Evaluated with parameters and scenario in scope; result rounded and clamped >= 0 (a token count).
Coloured place → row arrays Rows in colour element order; each row coerced through coerceTokenRecord (typed defaults for missing columns, extra columns throw).

code mode

One function body Returns { PlaceName: count | TokenRecord[] } keyed by place name (per_place uses place IDs — a known asymmetry). Unknown names are silently dropped.
Result: InitialMarking (JSON) Keyed by place ID. Fed to createSimulation, packed to binary in buildSimulation.

Metric compilation and evaluation

compileHirArtifacts Lower, typecheck, and emit a buffer-native metric program.
createHirMetricEvaluator Bind referenced place ordinals and the per-run string pool.
Per-frame evaluation Quick sim: evaluated on the main thread against stored frames. Experiments: evaluated inside the MC worker per run per frame (metrics/specs.ts).

Scenario sandboxing (authoring/sandbox.ts)

Distributions (deferred sampling)

HIR-emitted kernels construct small runtime values for Distribution.Gaussian / Uniform / Lognormal. Kernels defer sampling through the ABI sink; the engine samples each value once per token with the seeded RNG (sample-distribution.ts), so chained .map(fn) transforms share a single draw and runs stay reproducible. When stochasticity is disabled, the runtime isn’t injected and plain values are required.

Compatibility seam. HIR artifacts contain a version and fingerprint of their sanitized compilation input. Any schema, code, or extension change requires recompilation before the engine will run them.