@earthsciml/ast 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * and run json-schema-to-typescript to regenerate this file.
5
5
  */
6
6
  /**
7
- * EarthSciML Serialization Format (v1.0.0) — a language-agnostic JSON format for Earth system model components, their composition, and runtime configuration. v1.0.0 is a clean break with no deprecation path and no compatibility shims (docs/content/rfcs/unified-variable-model.md): the five declared variable types collapse to TWO. `unknown` is a quantity the solver solves for — its behavior is given by EQUATIONS and never by a field on the variable — and `parameter` is a quantity supplied to the solver, carrying a number or a `distribution` plus an optional `update` block saying when and from what it refreshes. Removed: `state` and `observed` (with the `expression` field — an observed variable becomes an unknown plus a bare-variable-LHS equation); `brownian` (with `noise_kind` / `correlation_group` — a noise source is a parameter with a `distribution` and `update.kind: "wiener"`, correlated noise being ONE vector-valued parameter whose distribution carries a `cov` matrix, which closes the gap the 0.x schema explicitly deferred); `discrete` (with `refresh` and the `RefreshTrigger` def — a refreshing input is a parameter with a `schedule` / `data` / `remesh` update); the `discrete_parameters` event lists and the event `functional_affect` (parameter mutation moves onto the parameter itself as a `condition` / `crossing` update carrying an `expression`, a `from` binding, or a `handler`; events now affect UNKNOWNS only); and the `data_loaders` COMPONENT kind together with `DataLoader.variables` (top-level `data_sources` is now a pure registry of ingest configuration — source, temporal, select, record_filter, extent, reader_options, determinism — that parameters draw from via `update.from`; a data source is no longer a coupling endpoint, a subsystem, or a scoped-name path root). Everything the solver needs is DERIVED rather than declared — which unknowns are ODE states, observed, or algebraic, and which parameters are Brownian, discrete, sampled, or constant — and every binding exposes the same classification functions to derive it (esm-spec §6.3.1). v0.9.0 changes no on-disk shape: it replaces the Option A (always-expanded) template round-trip with Option B (reference-preserving; esm-spec §9.6.4, docs/content/rfcs/out-of-line-expression-templates.md). Template references survive load and parse-then-emit; emit materializes each component's referenced templates into its `expression_templates` registry (authored entries first in authored order, materialized entries after in lexicographic UTF-8 name order; registry keys may be dotted post-rename names); eager references (target-bearing, esm-spec §9.6.4 rule 3) still expand at load, so no rewrite-target op survives inside a reference; `emit ∘ load` is a byte-wise fixed point; emitted documents carrying surviving references or materialized registries declare `esm: 0.9.0` or later. v0.8.0 is a clean break that removes all bespoke spatial-grid machinery in favor of expressing grid geometry directly with the unified Functional Aggregate Query (`aggregate`) IR (RFC semiring-faq-unified-ir), and retains no backward-compatibility shims. Removed: the top-level `grids`, `staggering_rules`, and `discretizations` blocks; the `Grid` / `GridExtent` / `GridMetricArray` / `GridMetricGenerator` / `GridConnectivity` / `GridCRS` defs; the entire stencil-template discretization rule grammar (`Discretization`, `DiscretizationVariant`, `MultiOutputStencilRule`, `DiscretizationRef`, `GridDiscretizationDescriptor`, and the `Rule` / `PatternNode` / `NeighborSelector` / `StencilEntry` / `RuleBinding` / `BoundaryPolicy` / `BoundaryPolicySpec` / `GhostWidth` / `GhostVarDecl` / `RuleRegion` / `RuleGuard` machinery, including `Equation.region`); all regridding configuration (`Model.regrid`, `RegridSpec`, `Interface.regridding`); the `Domain.spatial` / `SpatialDimension` / `CoordinateTransform` geometry block and the deprecated domain-level `boundary_conditions`; the `DataLoader.grid` / `DataLoader.mesh` descriptors and the `DataLoaderMesh` def; and the `grid_discretization_descriptor` document kind with its `grid_refs` test/example fields. Grid geometry — coordinates, extents, spacing, CRS parameters, connectivity, and metric arrays — is now ordinary data: loaded from a `data_loaders` primitive or declared as variables/parameters, with topology and metrics constructed declaratively as `aggregate` FAQs (the `intersect_polygon` kernel leaf remains for polygon clipping). Iteration domains are declared once in the document-scoped `index_sets` registry. Regridding is expressed as an ordinary coupling expression between two variables. Earlier additive features are retained: the `integral` AST op (PIDEs), array-or-single `plots.y`, sampled `function_tables` + `table_lookup`, in-file `expression_templates` + `apply_expression_template`, and the closed `enums` + `fn` / `enum` ops. The template-library RFC (docs/content/rfcs/template-library-imports.md; esm-spec §9.7) adds cross-file template sharing at esm 0.8.0: template-library files (top-level `expression_templates`), ordered `expression_template_imports` with §4.7 reference semantics, and load-time integer `metaparameters` admissible in `index_sets` sizes, `aggregate` dense ranges, and `makearray` regions — all resolved and folded at load, before validation and before the §9.6.3 rewrite fixpoint.
7
+ * EarthSciML Abstract Syntax Tree Format — v1.2.0 adds an OPTIONAL declared `units` string on a `const` expression node (esm-spec §4.8.5): the unit the constant's value is in, so a conversion constant such as `{"op": "const", "args": [], "value": 0.44704, "units": "m*h/(mi*s)"}` makes its equation dimensionally checkable. Purely additive: `units` is legal on no other node, and a document declaring esm below 1.2.0 that carries it is rejected with `const_units_version_too_old`. v1.1.0 renames the unified query node from `aggregate` to `faq`, the Functional Aggregate Query it has always been (docs/content/rfcs/faq-node-rename.md). `aggregate` names only one of the node's specializations: it also joins, filters, and — under `bool_and_or` with `distinct` — produces an index set rather than an array, reducing nothing. `faq` is the canonical tag; `aggregate` is accepted as a DEPRECATED ALIAS, normalized to `faq` at load with the warning `deprecated_op_alias`, and REMOVED at esm 2.0.0. The text surface is unchanged: the head keyword there is the ⊕-word (`sum`/`prod`/`max`/`min`/`any`), not the node name. The `arrayop` alias — nominally removed at 0.8.0 but still live in all five bindings — is removed for real. Also at v1.1.0: the top-level `solver` block. (v1.0.0) — a `faq`'s `join` clause carries the optional `syms`, naming the two RANGE SYMBOLS its `on` pairs are read at, so a relation can be joined to ITSELF: two ranges over one index set, whose key columns' shared axis cannot say which side is which (CONFORMANCE_SPEC §5.5.8, docs/content/rfcs/self-join-two-ranges-over-one-index-set.md). Purely additive — every document written before it stays valid and unchanged, which is why it lands without a version bump; see that note's §4. v1.0.0 — a language-agnostic JSON format for Earth system model components, their composition, and runtime configuration. v1.0.0 is a clean break with no deprecation path and no compatibility shims (docs/content/rfcs/unified-variable-model.md): the five declared variable types collapse to TWO. `unknown` is a quantity the solver solves for — its behavior is given by EQUATIONS and never by a field on the variable — and `parameter` is a quantity supplied to the solver, carrying a number or a `distribution` plus an optional `update` block saying when and from what it refreshes. Removed: `state` and `observed` (with the `expression` field — an observed variable becomes an unknown plus a bare-variable-LHS equation); `brownian` (with `noise_kind` / `correlation_group` — a noise source is a parameter with a `distribution` and `update.kind: "wiener"`, correlated noise being ONE vector-valued parameter whose distribution carries a `cov` matrix, which closes the gap the 0.x schema explicitly deferred); `discrete` (with `refresh` and the `RefreshTrigger` def — a refreshing input is a parameter with a `schedule` / `data` / `remesh` update); the `discrete_parameters` event lists and the event `functional_affect` (parameter mutation moves onto the parameter itself as a `condition` / `crossing` update carrying an `expression`, a `from` binding, or a `handler`; events now affect UNKNOWNS only); and the `data_loaders` COMPONENT kind together with `DataLoader.variables` (top-level `data_sources` is now a pure registry of ingest configuration — source, temporal, select, record_filter, extent, reader_options, determinism — that parameters draw from via `update.from`; a data source is no longer a coupling endpoint, a subsystem, or a scoped-name path root). Everything the solver needs is DERIVED rather than declared — which unknowns are ODE states, observed, or algebraic, and which parameters are Brownian, discrete, sampled, or constant — and every binding exposes the same classification functions to derive it (esm-spec §6.3.1). v0.9.0 changes no on-disk shape: it replaces the Option A (always-expanded) template round-trip with Option B (reference-preserving; esm-spec §9.6.4, docs/content/rfcs/out-of-line-expression-templates.md). Template references survive load and parse-then-emit; emit materializes each component's referenced templates into its `expression_templates` registry (authored entries first in authored order, materialized entries after in lexicographic UTF-8 name order; registry keys may be dotted post-rename names); eager references (target-bearing, esm-spec §9.6.4 rule 3) still expand at load, so no rewrite-target op survives inside a reference; `emit ∘ load` is a byte-wise fixed point; emitted documents carrying surviving references or materialized registries declare `esm: 0.9.0` or later. v0.8.0 is a clean break that removes all bespoke spatial-grid machinery in favor of expressing grid geometry directly with the unified Functional Aggregate Query (`faq`) IR (RFC semiring-faq-unified-ir), and retains no backward-compatibility shims. Removed: the top-level `grids`, `staggering_rules`, and `discretizations` blocks; the `Grid` / `GridExtent` / `GridMetricArray` / `GridMetricGenerator` / `GridConnectivity` / `GridCRS` defs; the entire stencil-template discretization rule grammar (`Discretization`, `DiscretizationVariant`, `MultiOutputStencilRule`, `DiscretizationRef`, `GridDiscretizationDescriptor`, and the `Rule` / `PatternNode` / `NeighborSelector` / `StencilEntry` / `RuleBinding` / `BoundaryPolicy` / `BoundaryPolicySpec` / `GhostWidth` / `GhostVarDecl` / `RuleRegion` / `RuleGuard` machinery, including `Equation.region`); all regridding configuration (`Model.regrid`, `RegridSpec`, `Interface.regridding`); the `Domain.spatial` / `SpatialDimension` / `CoordinateTransform` geometry block and the deprecated domain-level `boundary_conditions`; the `DataLoader.grid` / `DataLoader.mesh` descriptors and the `DataLoaderMesh` def; and the `grid_discretization_descriptor` document kind with its `grid_refs` test/example fields. Grid geometry — coordinates, extents, spacing, CRS parameters, connectivity, and metric arrays — is now ordinary data: loaded from a `data_loaders` primitive or declared as variables/parameters, with topology and metrics constructed declaratively as `faq` nodes (the `intersect_polygon` kernel leaf remains for polygon clipping). Iteration domains are declared once in the document-scoped `index_sets` registry. Regridding is expressed as an ordinary coupling expression between two variables. Earlier additive features are retained: the `integral` AST op (PIDEs), array-or-single `plots.y`, sampled `function_tables` + `table_lookup`, in-file `expression_templates` + `apply_expression_template`, and the closed `enums` + `fn` / `enum` ops. The template-library RFC (docs/content/rfcs/template-library-imports.md; esm-spec §9.7) adds cross-file template sharing at esm 0.8.0: template-library files (top-level `expression_templates`), ordered `expression_template_imports` with §4.7 reference semantics, and load-time integer `metaparameters` admissible in `index_sets` sizes, `faq` dense ranges, and `makearray` regions — all resolved and folded at load, before validation and before the §9.6.3 rewrite fixpoint.
8
8
  */
9
9
  type ESMFormat = ESMFormat1 & ESMFormat2;
10
10
  type ESMFormat1 = {
@@ -20,13 +20,17 @@ type ModelVariable$1 = ModelVariable1 & {
20
20
  type: "unknown" | "parameter";
21
21
  units?: string;
22
22
  /**
23
- * For an unknown, its initial value at t=0. For a parameter, its constant value — mutually exclusive with `distribution`, which draws the value instead.
23
+ * For an unknown, its initial value at t=0. For a parameter, its constant value — mutually exclusive with `distribution`, which draws the value instead. A SHAPED variable (non-empty `shape`) may instead carry a row-major nested JSON array whose nesting matches the declared shape after metaparameter folding; a mismatch is a load-time error. A scalar on a shaped variable keeps its broadcast meaning — the one value applies to every element (esm-spec §6.3).
24
24
  */
25
- default?: number;
25
+ default?: number | NumericArrayLiteral;
26
26
  /**
27
27
  * Units of the default value, if different from the declared units field. When present, validators flag a unit_inconsistency error if these do not match the declared units (including dimensionally incompatible cases like K vs kg, and same-dimension mismatches like K vs degC). Default is the same as `units`.
28
28
  */
29
29
  default_units?: string;
30
+ /**
31
+ * Floating-point precision of THIS variable and of the arithmetic over it, overriding the document-wide `domain.element_type` (esm-spec §11.3.1). It exists for the key/quantity split one document-wide precision cannot express: a relational model whose floating-point QUANTITIES are binary32 still has join keys and integer identifiers binary32 cannot represent (a ten-digit category code is ~2.26e9, 135x binary32's exact-integer limit of 2^24), so the keys declare `Float64` while the quantities follow the document. Precision propagates from the leaves: an operator evaluates at its operands' precision, a numeric literal adopts its context's, and an operator whose operands carry DIFFERENT precisions is an error naming both variables — never a silent widening. Omitted means the document's `domain.element_type`.
32
+ */
33
+ element_type?: "Float32" | "Float64";
30
34
  description?: string;
31
35
  /**
32
36
  * Arrayed-variable shape: ordered list of index-set names drawn from the document-scoped `index_sets` registry. Omitted or null indicates a scalar. REQUIRED for a parameter whose `update` is `schedule`, `data`, or `remesh` — such a parameter is a buffer whose extent is fixed at setup and refilled on a discrete cadence, so its extent must be known ahead of time (RFC semiring-faq-unified-ir §6.1); an empty array is a valid (scalar) shape.
@@ -64,6 +68,10 @@ type ModelVariable$1 = ModelVariable1 & {
64
68
  type ModelVariable1 = {
65
69
  [k: string]: unknown;
66
70
  };
71
+ /**
72
+ * A row-major nested JSON array of numbers carrying a SHAPED variable's inline data (esm-spec §6.3, §6.6.2). Every axis is a JSON array and every leaf a number; the nesting depth and the per-axis lengths MUST match the variable's declared `shape` after metaparameter folding, and a mismatch is a load-time error — the same convention `from_file` data already follows (§6.6.5 convention 3). A SCALAR in the same position keeps its broadcast meaning (one value applied to every element).
73
+ */
74
+ type NumericArrayLiteral = (number | NumericArrayLiteral)[];
67
75
  /**
68
76
  * Declares WHEN a parameter refreshes and WHAT it refreshes from. A parameter with no `update` is a constant (or, with a `distribution`, is sampled once at setup). Six kinds. `wiener` is a driving stochastic process and takes no value form — it resamples the parameter's own `distribution`. The other five each take EXACTLY ONE value form: an `expression`, a `from` binding to a data source, or a registered `handler`. Together they subsume three constructs the 0.x format kept apart: the `brownian` variable type (now `wiener`), the `discrete` type with its `RefreshTrigger` (now `schedule` / `data` / `remesh`), and the `discrete_parameters` event lists with their `functional_affect` (now `condition` / `crossing`). This is also the sole seed of the DISCRETE cadence class in the dependency-partition pass (RFC semiring-faq-unified-ir §6.1): without it such inputs would be mis-seeded as CONST (never refresh) or CONTINUOUS (recompute every step).
69
77
  */
@@ -81,11 +89,11 @@ type Expression = number | string | ExpressionNode;
81
89
  */
82
90
  type ExpressionNode = ExpressionNode1 & {
83
91
  /**
84
- * Operator name. TWO TIERS (esm-spec §4.2). (1) The CLOSED evaluable-core set — every binding's evaluator implements each one directly: `+ - * / ^`, the comparisons/booleans (`> < >= <= == != and or not`), the elementary functions (`exp log log10 sqrt abs sign sin cos tan asin acos atan atan2 sinh cosh tanh asinh acosh atanh min max floor ceil`), `ifelse`, the calculus form-ops `D` and `ic` (structural / equation-LHS use only), `Pre`, `true`, and the array/query ops (`aggregate makearray index broadcast reshape transpose concat skolem rank argmin argmax intersect_polygon polygon_intersection_area fn enum const table_lookup apply_expression_template`). (2) The OPEN rewrite-target tier — ANY other identifier: a spatial `D` on a right-hand side, the optional sugar ops `grad`/`div`/`laplacian`/`integral`, or a user op such as `godunov_hamiltonian`. A rewrite-target op carries NO evaluator implementation and MUST be eliminated by a rewrite rule (§9.6) before evaluation. Loading is permissive — a file MAY load with rewrite-target ops still present (e.g. a coupling/scoping example that never simulates) — but one reaching evaluation/compilation without being lowered is rejected with `unlowered_operator`. The `pattern` only rejects malformed strings — it does NOT enforce membership in the core set, so the open tier stays expressible. The evaluable-core set is the ONLY set the format privileges.
92
+ * Operator name. TWO TIERS (esm-spec §4.2). (1) The CLOSED evaluable-core set — every binding's evaluator implements each one directly: `+ - * / ^` and `pow` (the word spelling of `^`), the comparisons/booleans (`> < >= <= == != and or not`), the elementary functions (`exp log log10 sqrt abs sign sin cos tan asin acos atan atan2 sinh cosh tanh asinh acosh atanh min max floor ceil`), `ifelse`, the calculus form-ops `D` and `ic` (structural / equation-LHS use only), `Pre`, the boolean literals `true` and `false`, and the array/query ops (`faq makearray index broadcast reshape transpose concat skolem rank argmin argmax intersect_polygon polygon_intersection_area fn enum const table_lookup apply_expression_template`). (2) The OPEN rewrite-target tier — ANY other identifier: a spatial `D` on a right-hand side, the optional sugar ops `grad`/`div`/`laplacian`/`integral`, or a user op such as `godunov_hamiltonian`. A rewrite-target op carries NO evaluator implementation and MUST be eliminated by a rewrite rule (§9.6) before evaluation. Loading is permissive — a file MAY load with rewrite-target ops still present (e.g. a coupling/scoping example that never simulates) — but one reaching evaluation/compilation without being lowered is rejected with `unlowered_operator`. The `pattern` only rejects malformed strings — it does NOT enforce membership in the core set, so the open tier stays expressible. The evaluable-core set is the ONLY set the format privileges. The array/query node `faq` is a FUNCTIONAL AGGREGATE QUERY (docs/content/rfcs/semiring-faq-unified-ir.md): a semiring reduction of a body over named index sets, carrying joins, filters and set semantics, so under `bool_and_or` + `distinct` it produces an INDEX SET rather than an array and reduces nothing. DEPRECATED ALIAS: `aggregate` is the pre-1.1.0 spelling of `faq`. A loader MUST accept it, MUST normalize the node to `faq` before validation so that nothing downstream of the loader sees the alias, and MUST report the warning diagnostic `deprecated_op_alias` once per document per alias. It is REMOVED at esm 2.0.0 (docs/content/rfcs/faq-node-rename.md). The older `arrayop` spelling is NOT accepted. It was removed at 0.8.0 and is rejected BY NAME with the hard error `removed_op` — NOT left to the open tier: `arrayop` is a well-formed identifier, so the open rewrite-target tier would otherwise admit it and the document would load silently, failing much later as `unlowered_operator`, or never. A loader MUST also reject a document that spells `faq` while declaring esm < 1.1.0 (`faq_version_too_old`), the same version gate the top-level `solver` block follows; the gate reads the AUTHORED form, so a pre-1.1.0 document carrying the `aggregate` alias stays legal and is normalized with its declared version raised to the 1.1.0 floor.
85
93
  */
86
94
  op: string;
87
95
  /**
88
- * Stable node identity (RFC semiring-faq-unified-ir §6.1): an optional, author-assigned identifier that makes this expression node addressable as a vertex in the inter-node dependency DAG the partition pass walks. Its primary use is to be the referent of a derived index set: an `index_sets` entry of kind "derived" names, via `from_faq`, the index-set-producing `aggregate` node that materializes it — and it names it by this id. When present it MUST be unique among the expression nodes of its model; the build-time reference-resolution pass errors on a duplicate id and on a `from_faq` that names no node. Absent ⇒ the node is addressed only by its structural path and cannot be the target of a `from_faq`. Purely additive: a file using no `id` validates and resolves exactly as before.
96
+ * Stable node identity (RFC semiring-faq-unified-ir §6.1): an optional, author-assigned identifier that makes this expression node addressable as a vertex in the inter-node dependency DAG the partition pass walks. Its primary use is to be the referent of a derived index set: an `index_sets` entry of kind "derived" names, via `from_faq`, the index-set-producing `faq` node that materializes it — and it names it by this id. When present it MUST be unique among the expression nodes of its DOCUMENT — `index_sets` is a document-scoped registry, so a `from_faq` in it resolves against every model's nodes, and per-model uniqueness would leave a cross-model reference ambiguous. The build-time reference-resolution pass errors on a duplicate id and on a `from_faq` that names no node in the document. Absent ⇒ the node is addressed only by its structural path and cannot be the target of a `from_faq`. Purely additive: a file using no `id` validates and resolves exactly as before.
89
97
  */
90
98
  id?: string;
91
99
  /**
@@ -93,7 +101,7 @@ type ExpressionNode = ExpressionNode1 & {
93
101
  */
94
102
  expect_cadence?: "const" | "discrete" | "continuous";
95
103
  /**
96
- * Operand list. For most ops these are sub-expressions. Array ops use args for the input array operands (aggregate, broadcast, index, reshape, transpose, concat). makearray has no natural args and uses an empty array. For `D` (esm-spec §4.2 "Arity of `D`"): `args[0]` is always the differentiated operand; a STRUCTURAL `D` (`wrt` "t", or `wrt` absent) is strictly unary, while a REWRITE-TARGET `D` (spatial `wrt`) MAY carry an unbounded number of TRAILING AUXILIARY OPERANDS after `args[0]` — the per-face boundary/halo data a discretization rule binds and consumes (§9.6.8). Trailing operands carry NO evaluator semantics and no per-position meaning assigned by this format; they are matched positionally by the consuming rule.
104
+ * Operand list. For most ops these are sub-expressions. Array ops use args for the input array operands (faq, broadcast, index, reshape, transpose, concat). makearray has no natural args and uses an empty array. For `D` (esm-spec §4.2 "Arity of `D`"): `args[0]` is always the differentiated operand; a STRUCTURAL `D` (`wrt` "t", or `wrt` absent) is strictly unary, while a REWRITE-TARGET `D` (spatial `wrt`) MAY carry an unbounded number of TRAILING AUXILIARY OPERANDS after `args[0]` — the per-face boundary/halo data a discretization rule binds and consumes (§9.6.8). Trailing operands carry NO evaluator semantics and no per-position meaning assigned by this format; they are matched positionally by the consuming rule.
97
105
  *
98
106
  * @minItems 0
99
107
  */
@@ -125,7 +133,7 @@ type ExpressionNode = ExpressionNode1 & {
125
133
  */
126
134
  upper?: Expression;
127
135
  /**
128
- * For aggregate: the result's index signature. Each entry is either a string (a symbolic index variable like "i", "j") or the integer 1 (a literal singleton dimension for reshape/broadcast). Mirrors SymbolicUtils.ArrayOp.output_idx.
136
+ * For faq: the result's index signature. Each entry is either a string (a symbolic index variable like "i", "j") or the integer 1 (a literal singleton dimension for reshape/broadcast). Mirrors SymbolicUtils.ArrayOp.output_idx.
129
137
  */
130
138
  output_idx?: (string | 1)[];
131
139
  /**
@@ -133,15 +141,15 @@ type ExpressionNode = ExpressionNode1 & {
133
141
  */
134
142
  expr?: Expression;
135
143
  /**
136
- * For aggregate: the reduction operator applied to any index symbol that appears in expr but not in output_idx. Default is "+". This names only the ⊕ operator; it is a shorthand retained for files that omit "semiring" (⊕ = reduce, ⊗ = "*"). When "semiring" is present it supersedes "reduce".
144
+ * For faq: the reduction operator applied to any index symbol that appears in expr but not in output_idx. Default is "+". This names only the ⊕ operator; it is a shorthand retained for files that omit "semiring" (⊕ = reduce, ⊗ = "*"). When "semiring" is present it supersedes "reduce".
137
145
  */
138
146
  reduce?: "+" | "*" | "max" | "min";
139
147
  /**
140
- * For aggregate: the named semiring (⊕, ⊗) that parameterizes the reduction (RFC semiring-faq-unified-ir §5.1). A closed, exhaustive registry — adding a semiring is a spec change, not a per-file extension. Absent ⇒ "sum_product", which reproduces today's einsum / sum-of-products semantics exactly (the strict-superset promise). The ⊕ operator, the ⊗ operator, and BOTH identity elements (the value of an empty ⊕-reduction and an empty ⊗-product) are fixed by the registry table and MUST NOT be written into the file: sum_product (+,0;×,1), max_product (max,-∞;×,1), min_sum (min,+∞;+,0), max_sum (max,-∞;+,0), bool_and_or (∨,false;∧,true).
148
+ * For faq: the named semiring (⊕, ⊗) that parameterizes the reduction (RFC semiring-faq-unified-ir §5.1). A closed, exhaustive registry — adding a semiring is a spec change, not a per-file extension. Absent ⇒ "sum_product", which reproduces today's einsum / sum-of-products semantics exactly (the strict-superset promise). The ⊕ operator, the ⊗ operator, and BOTH identity elements (the value of an empty ⊕-reduction and an empty ⊗-product) are fixed by the registry table and MUST NOT be written into the file: sum_product (+,0;×,1), max_product (max,-∞;×,1), min_sum (min,+∞;+,0), max_sum (max,-∞;+,0), bool_and_or (∨,false;∧,true).
141
149
  */
142
150
  semiring?: "sum_product" | "max_product" | "min_sum" | "max_sum" | "bool_and_or";
143
151
  /**
144
- * For aggregate: optional map from index symbol name to the range it iterates over. Each value is EITHER (a) a dense integer tuple — a 2-element array [start, stop] (unit step) or a 3-element array [start, step, stop], as today, mirroring SymbolicUtils.ArrayOp.ranges — entries MAY be metaparameter expressions folded to concrete integers at load (esm-spec §9.7.6); OR (b) a reference to a declared index set (RFC semiring-faq-unified-ir §5.2): an object { "from": <index_sets key> }, optionally with "of": [parent index names] for a ragged / dependent inner set (e.g. the edges of cell "i"). Indices not present are inferred from the domain / operand shapes at runtime.
152
+ * For faq: optional map from index symbol name to the range it iterates over. Each value is EITHER (a) a dense integer tuple — a 2-element array [start, stop] (unit step) or a 3-element array [start, step, stop], as today, mirroring SymbolicUtils.ArrayOp.ranges — entries MAY be metaparameter expressions folded to concrete integers at load (esm-spec §9.7.6); OR (b) a reference to a declared index set (RFC semiring-faq-unified-ir §5.2): an object { "from": <index_sets key> }, optionally with "of": [parent index names] for a ragged / dependent inner set (e.g. the edges of cell "i"). Indices not present are inferred from the domain / operand shapes at runtime.
145
153
  */
146
154
  ranges?: {
147
155
  [k: string]: [MetaparameterExpression, MetaparameterExpression] | [MetaparameterExpression, MetaparameterExpression, MetaparameterExpression] | {
@@ -150,13 +158,13 @@ type ExpressionNode = ExpressionNode1 & {
150
158
  */
151
159
  from: string;
152
160
  /**
153
- * Parent index name(s) for a ragged / dependent inner set: the members enumerated depend on these outer indices (e.g. { "from": "edges_of_cell", "of": ["i"] } iterates the edges of cell i).
161
+ * Parent index name(s) for a ragged / dependent inner set: the enumeration depends on these outer indices (e.g. { "from": "edges_of_cell", "of": ["i"] } iterates the POSITIONS k in 1…offsets[i] of cell i's edges, and the body gathers each edge as index(values, i, k); esm-spec §4.3.1 "Ragged ranges").
154
162
  */
155
163
  of?: string[];
156
164
  };
157
165
  };
158
166
  /**
159
- * For aggregate: optional value-equality combination of factors by key columns — an inner equi-join (RFC semiring-faq-unified-ir §5.3), subsuming ESI join and making connectivity gathers first-class. Each entry is a join clause whose "on" lists one or more key-column pairs [left, right]; a ⊗-product term is contributed only for index combinations whose key columns are equal on EVERY listed pair, and an unmatched row contributes the additive identity (the empty ⊕-reduction value), adding nothing under any semiring. Inner-only — there is no outer/left variant; many-to-many is defined, not an error (m·n product terms, each a ⊗-term into the enclosing ⊕-reduction); key columns must be exact-equality types (integer IDs or categorical members compared by Unicode code point) — floating-point join keys are forbidden. Absent ⇒ factors combine only by shared index name (positional einsum), exactly as today.
167
+ * For faq: optional value-equality combination of factors by key columns — an inner equi-join (RFC semiring-faq-unified-ir §5.3), subsuming ESI join and making connectivity gathers first-class. Each entry is a join clause whose "on" lists one or more key-column pairs [left, right]; a ⊗-product term is contributed only for index combinations whose key columns are equal on EVERY listed pair, and an unmatched row contributes the additive identity (the empty ⊕-reduction value), adding nothing under any semiring. Inner-only — there is no outer/left variant; many-to-many is defined, not an error (m·n product terms, each a ⊗-term into the enclosing ⊕-reduction); key columns must be exact-equality types (integer IDs or categorical members compared by Unicode code point) — floating-point join keys are forbidden. Absent ⇒ factors combine only by shared index name (positional einsum), exactly as today. A key column may name a loop symbol, an index set, or a genuine 1-D DATA COLUMN, and the resolved match set DRIVES enumeration rather than merely testing it (CONFORMANCE_SPEC §5.5.8).
160
168
  */
161
169
  join?: ({
162
170
  [k: string]: unknown;
@@ -168,7 +176,7 @@ type ExpressionNode = ExpressionNode1 & {
168
176
  */
169
177
  filter?: Expression;
170
178
  /**
171
- * For aggregate: when true, the node has set semantics (dedup) and is index-set-producing — it materializes a data-derived index set rather than an array (RFC semiring-faq-unified-ir §5.5). Only meaningful under the `bool_and_or` semiring (the relational specialization, §5.1); combined with `key`, it enumerates the unique Skolem terms (e.g. the unique edges discovered from a face→vertex relation) and exposes the result as a `kind:"derived"` index set (§5.2) that a downstream geometric aggregate consumes. The output order is fixed by the normative determinism rules (§5.7): sort by the total tuple order, then drop adjacent duplicates — never first-seen / insertion order. Absent ⇒ false (ordinary array-producing reduction), exactly as today.
179
+ * For faq: when true, the node has set semantics (dedup) and is index-set-producing — it materializes a data-derived index set rather than an array (RFC semiring-faq-unified-ir §5.5). Only meaningful under the `bool_and_or` semiring (the relational specialization, §5.1); combined with `key`, it enumerates the unique Skolem terms (e.g. the unique edges discovered from a face→vertex relation) and exposes the result as a `kind:"derived"` index set (§5.2) that a downstream geometric `faq` consumes. The output order is fixed by the normative determinism rules (§5.7): sort by the total tuple order, then drop adjacent duplicates — never first-seen / insertion order. Absent ⇒ false (ordinary array-producing reduction), exactly as today.
172
180
  */
173
181
  distinct?: boolean;
174
182
  /**
@@ -216,7 +224,7 @@ type ExpressionNode = ExpressionNode1 & {
216
224
  */
217
225
  axis?: number;
218
226
  /**
219
- * For broadcast: the name of the scalar operator to apply element-wise to the operands in args (esm-spec.md §4.3.4). `{op: 'broadcast', fn: F, args: A}` means exactly what `{op: F, args: A}` means, applied element-wise — so a ONE-operand broadcast applies `fn` UNARILY (`broadcast(fn:'-',[x])` is `-x`), and MUST NOT be treated as the identity on its operand. The value MUST be an ExpressionNode op name drawn from the closed SCALAR subset — the pointwise ops: arithmetic (`+ - * / ^ neg`), elementary functions (`exp log ln log10 sqrt abs sign floor ceil sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh atan2 min max`), comparisons (`== != < <= > >=`), logical connectives (`and or not`), and `ifelse`. It may NOT name a non-pointwise op (`aggregate makearray index broadcast reshape transpose concat fn D ic Pre const true enum table_lookup apply_expression_template skolem rank distinct argmin argmax intersect_polygon polygon_intersection_area`), and `args.length` MUST satisfy the named operator's §4.2 arity. Bindings MUST reject a violation — including an absent `fn`, for which there is no default — with diagnostic 'invalid_broadcast_fn'; loading MUST fail. This is a VALUE constraint the schema deliberately does not enum-enforce, for the same reason `op` and the closed-registry `name` are not enum-enforced: the admissible set is fixed by the file's declared `esm` version, and the check must also cover arity, which a schema `enum` cannot express.
227
+ * For broadcast: the name of the scalar operator to apply element-wise to the operands in args (esm-spec.md §4.3.4). `{op: 'broadcast', fn: F, args: A}` means exactly what `{op: F, args: A}` means, applied element-wise — so a ONE-operand broadcast applies `fn` UNARILY (`broadcast(fn:'-',[x])` is `-x`), and MUST NOT be treated as the identity on its operand. The value MUST be an ExpressionNode op name drawn from the closed SCALAR subset — the pointwise ops: arithmetic (`+ - * / ^ neg`), elementary functions (`exp log ln log10 sqrt abs sign floor ceil sin cos tan asin acos atan sinh cosh tanh asinh acosh atanh atan2 min max`), comparisons (`== != < <= > >=`), logical connectives (`and or not`), and `ifelse`. It may NOT name a non-pointwise op (`faq makearray index broadcast reshape transpose concat fn D ic Pre const true enum table_lookup apply_expression_template skolem rank distinct argmin argmax intersect_polygon polygon_intersection_area`), and `args.length` MUST satisfy the named operator's §4.2 arity. Bindings MUST reject a violation — including an absent `fn`, for which there is no default — with diagnostic 'invalid_broadcast_fn'; loading MUST fail. This is a VALUE constraint the schema deliberately does not enum-enforce, for the same reason `op` and the closed-registry `name` are not enum-enforced: the admissible set is fixed by the file's declared `esm` version, and the check must also cover arity, which a schema `enum` cannot express.
220
228
  */
221
229
  fn?: string;
222
230
  /**
@@ -229,6 +237,10 @@ type ExpressionNode = ExpressionNode1 & {
229
237
  value?: {
230
238
  [k: string]: unknown;
231
239
  };
240
+ /**
241
+ * For const only: the declared units of the constant's `value`, resolved against the esm-spec §4.8.1 registry with the §4.8.2 grammar exactly as a variable's `units` are. A unit-bearing constant has that dimension and exact scale in dimensional analysis (esm-spec §4.8.5); a `const` without `units`, like a bare numeric literal, stays undeterminable. The numeric value is never checked against the units. Legal on no other op. Arrives at esm 1.2.0 (`const_units_version_too_old`).
242
+ */
243
+ units?: string;
232
244
  /**
233
245
  * For table_lookup: the id of the function_tables entry to evaluate. Bindings MUST reject references to undeclared tables at file-load time with diagnostic 'table_lookup_unknown_table'.
234
246
  */
@@ -254,7 +266,7 @@ type ExpressionNode1 = {
254
266
  [k: string]: unknown;
255
267
  };
256
268
  /**
257
- * A load-time integer expression over metaparameters (esm-spec §9.7.6): an integer literal, a declared metaparameter name, or `{op: +|-|*|/, args: [...]}` over metaparameter expressions (unary `-` allowed). Folded to a concrete integer at load with exact 64-bit arithmetic; `/` MUST divide exactly and overflow is an error (`metaparameter_type_error`). Admissible in structural integer sites (`index_sets` interval `size`, `aggregate` dense `ranges` tuple entries, `makearray` `regions` bound pairs) AND as an import-edge / subsystem-edge binding VALUE, whose free names resolve in the importing document's metaparameter scope.
269
+ * A load-time integer expression over metaparameters (esm-spec §9.7.6): an integer literal, a declared metaparameter name, or `{op: +|-|*|/, args: [...]}` over metaparameter expressions (unary `-` allowed). Folded to a concrete integer at load with exact 64-bit arithmetic; `/` MUST divide exactly and overflow is an error (`metaparameter_type_error`). Admissible in structural integer sites (`index_sets` interval `size`, `faq` dense `ranges` tuple entries, `makearray` `regions` bound pairs) AND as an import-edge / subsystem-edge binding VALUE, whose free names resolve in the importing document's metaparameter scope.
258
270
  */
259
271
  type MetaparameterExpression = number | string | {
260
272
  op: "+" | "-" | "*" | "/";
@@ -289,36 +301,7 @@ type DiscreteEventTrigger = {
289
301
  times: [number, ...number[]];
290
302
  };
291
303
  /**
292
- * One native axis of a `DataLoaderSelect`. The vocabulary is shared with the projection-pushdown gate template (CONFORMANCE_SPEC §5.5): "all" keeps the axis whole; `fixed` takes one index and DROPS the axis; `range` takes a half-open strided window and keeps it; `gated_by` names a `derived` index set whose value-invention members become the axis, which DEFERS the fetch until those members exist.
293
- */
294
- type DataSourceSelectAxis = "all" | {
295
- /**
296
- * A single 0-based native index.
297
- */
298
- fixed: number | [number];
299
- } | {
300
- range: {
301
- /**
302
- * Inclusive first index (default 0). A string names a `metaparameters` entry and resolves to its default.
303
- */
304
- start?: number | string;
305
- /**
306
- * Exclusive last index. A string names a `metaparameters` entry and resolves to its default, so a prefix is declared in the model's own terms (`W[0:N_SRC]`) rather than as a repeated literal that can drift from the index set sized by the same metaparameter.
307
- */
308
- stop: number | string;
309
- /**
310
- * Stride (default 1; MUST be >= 1).
311
- */
312
- step?: number | string;
313
- };
314
- } | {
315
- /**
316
- * Name of a `kind: "derived"` index set. The axis becomes that set's materialised members in canonical (sorted) member order (CONFORMANCE_SPEC §5.5, Hook 2), so only the rows the model can reach are ever read.
317
- */
318
- gated_by: string;
319
- };
320
- /**
321
- * A single scalar check against a model variable at a specific (variable, time) point. PDE-aware variants pin a spatial point via `coords`, or reduce the field to a scalar via `reduce` (domain-integral, mean, max, min, or error-norm). `coords` and `reduce` are mutually exclusive; if neither is given the assertion is pointwise and only valid on a 0-D component. Error-norm reductions (L2_error, Linf_error) require `reference`.
304
+ * A single scalar check against a model variable at a specific (variable, time) point. PDE-aware variants pin a spatial point via `coords`, or reduce the field to a scalar via `reduce` (domain-integral, mean, max, min, or error-norm). `coords` and `reduce` are mutually exclusive; if neither is given the assertion is pointwise and is valid only on a variable declared without a `shape` or on an element name such as `u[1]` (esm-spec §6.6.5). Error-norm reductions (L2_error, Linf_error) require `reference`.
322
305
  */
323
306
  type Assertion = Assertion1 & {
324
307
  /**
@@ -468,6 +451,35 @@ type Parameter = Parameter1 & {
468
451
  type Parameter1 = {
469
452
  [k: string]: unknown;
470
453
  };
454
+ /**
455
+ * One native axis of a `DataLoaderSelect`. The vocabulary is shared with the projection-pushdown gate template (CONFORMANCE_SPEC §5.5): "all" keeps the axis whole; `fixed` takes one index and DROPS the axis; `range` takes a half-open strided window and keeps it; `gated_by` names a `derived` index set whose value-invention members become the axis, which DEFERS the fetch until those members exist.
456
+ */
457
+ type DataSourceSelectAxis = "all" | {
458
+ /**
459
+ * A single 0-based native index.
460
+ */
461
+ fixed: number | [number];
462
+ } | {
463
+ range: {
464
+ /**
465
+ * Inclusive first index (default 0). A string names a `metaparameters` entry and resolves to its closed value (esm-spec §9.7.6).
466
+ */
467
+ start?: number | string;
468
+ /**
469
+ * Exclusive last index. A string names a `metaparameters` entry and resolves to its closed value (esm-spec §9.7.6), so a prefix is declared in the model's own terms (`W[0:N_SRC]`) rather than as a repeated literal that can drift from the index set sized by the same metaparameter.
470
+ */
471
+ stop: number | string;
472
+ /**
473
+ * Stride (default 1; MUST be >= 1).
474
+ */
475
+ step?: number | string;
476
+ };
477
+ } | {
478
+ /**
479
+ * Name of a `kind: "derived"` index set. The axis becomes that set's materialised members in canonical (sorted) member order (CONFORMANCE_SPEC §5.5, Hook 2), so only the rows the model can reach are ever read.
480
+ */
481
+ gated_by: string;
482
+ };
471
483
  /**
472
484
  * A single coupling rule connecting models or reaction systems. A `data_sources` entry is not a component and cannot appear as a coupling endpoint; external data enters through a parameter's `update`.
473
485
  */
@@ -558,11 +570,11 @@ type CouplingEvent = CouplingEvent1 & {
558
570
  times: [number, ...number[]];
559
571
  };
560
572
  /**
561
- * Affect equations. Required unless functional_affect is used.
573
+ * Affect equations. Required: esm 1.0.0 removed the `functional_affect` alternative, so `affects` is the only affect channel. Every LHS MUST name an unknown (`event_affects_parameter` otherwise).
562
574
  */
563
575
  affects: AffectEquation[];
564
576
  affect_neg?: null | AffectEquation[];
565
- root_find?: "left" | "right" | "all";
577
+ root_find?: "left" | "right";
566
578
  reinitialize?: boolean;
567
579
  /**
568
580
  * Map from a target system referenced by this coupling entry to the template-library imports registered into THAT component's template scope (esm-spec §9.7.10) — assembler-chosen discretization for a PDE component as it is wired into the assembly. Each key MUST name a model/reaction-system this entry references (template_inject_target_unknown otherwise); a key resolving to neither a model nor a reaction system is template_inject_target_not_component -- which from 1.0.0 is also how a key naming a `data_sources` entry is reported, since a data source is not a component and the separate template_inject_target_is_loader code is retired. Values use the §9.7.2 entry shape. Load-time only; consumed by the §9.6.3 fixpoint; does not survive parse→emit.
@@ -578,15 +590,15 @@ type CouplingEvent1 = {
578
590
  [k: string]: unknown;
579
591
  };
580
592
  /**
581
- * A named index set (RFC semiring-faq-unified-ir §5.2): the declaration shape for an iteration domain referenced from an `aggregate` range via { "from": <name> }. Covers grid axes and categorical dimensions under one shape. Exactly one of four kinds, each requiring its own fields.
593
+ * A named index set (RFC semiring-faq-unified-ir §5.2): the declaration shape for an iteration domain referenced from a `faq` range via { "from": <name> }. Covers grid axes and categorical dimensions under one shape. Exactly one of four kinds, each requiring its own fields.
582
594
  */
583
595
  type IndexSet = IndexSet1 & {
584
596
  /**
585
- * Which of the four index-set forms this entry is. "interval": a dense [1..size] grid axis. "categorical": an explicit enumeration of members. "derived": a data-derived set materialized from an index-set-producing node (a `distinct` `aggregate`, or an `intersect_polygon` ring leaf whose clipped ring has a data-dependent vertex count, §8.1). "ragged": a per-parent (dependent) inner set backed by CSR offsets/values factors.
597
+ * Which of the four index-set forms this entry is. "interval": a dense [1..size] grid axis. "categorical": an explicit enumeration of members. "derived": a data-derived set materialized from an index-set-producing node (a `distinct` `faq`, or an `intersect_polygon` ring leaf whose clipped ring has a data-dependent vertex count, §8.1). "ragged": a per-parent (dependent) inner set backed by an `offsets` per-parent length factor and a padded `values` member factor; a `faq` range over it binds the POSITION k in 1…offsets[parent] (esm-spec §4.3.1 "Ragged ranges").
586
598
  */
587
599
  kind: "interval" | "categorical" | "derived" | "ragged";
588
600
  /**
589
- * A load-time integer expression over metaparameters (esm-spec §9.7.6): an integer literal, a declared metaparameter name, or `{op: +|-|*|/, args: [...]}` over metaparameter expressions (unary `-` allowed). Folded to a concrete integer at load with exact 64-bit arithmetic; `/` MUST divide exactly and overflow is an error (`metaparameter_type_error`). Admissible in structural integer sites (`index_sets` interval `size`, `aggregate` dense `ranges` tuple entries, `makearray` `regions` bound pairs) AND as an import-edge / subsystem-edge binding VALUE, whose free names resolve in the importing document's metaparameter scope.
601
+ * A load-time integer expression over metaparameters (esm-spec §9.7.6): an integer literal, a declared metaparameter name, or `{op: +|-|*|/, args: [...]}` over metaparameter expressions (unary `-` allowed). Folded to a concrete integer at load with exact 64-bit arithmetic; `/` MUST divide exactly and overflow is an error (`metaparameter_type_error`). Admissible in structural integer sites (`index_sets` interval `size`, `faq` dense `ranges` tuple entries, `makearray` `regions` bound pairs) AND as an import-edge / subsystem-edge binding VALUE, whose free names resolve in the importing document's metaparameter scope.
590
602
  */
591
603
  size?: number | string | {
592
604
  op: "+" | "-" | "*" | "/";
@@ -600,11 +612,11 @@ type IndexSet = IndexSet1 & {
600
612
  */
601
613
  members?: unknown[];
602
614
  /**
603
- * derived: the id of the index-set-producing node (RFC §5.5) that materializes this set, named by its `id`. Usually an `aggregate` node (`distinct: true`) — e.g. the unique edges discovered from a face→vertex relation. It MAY also be an `intersect_polygon` geometry-kernel leaf (RFC §8.1): the clipped overlap ring it returns has a data-dependent number of vertices, so the ring's vertex set is exactly such a derived index set, and `polygon_area` is then an ordinary `sum_product` FAQ over it. Required when kind is "derived".
615
+ * derived: the id of the index-set-producing node (RFC §5.5) that materializes this set, named by its `id`. Usually a `faq` node (`distinct: true`) — e.g. the unique edges discovered from a face→vertex relation. It MAY also be an `intersect_polygon` geometry-kernel leaf (RFC §8.1): the clipped overlap ring it returns has a data-dependent number of vertices, so the ring's vertex set is exactly such a derived index set, and `polygon_area` is then an ordinary `sum_product` FAQ over it. Required when kind is "derived".
604
616
  */
605
617
  from_faq?: string;
606
618
  /**
607
- * derived: name of the buffer that receives the surviving member key per invented position (CONFORMANCE_SPEC §5.5, Hook 1) — fed back as a `const` factor so an aggregate ranging over the compact derived axis can gather the full-grid rows that axis selects. That is what downstream aggregates do; it is not a claim about ORIENTATION — an aggregate that reduces OVER the compact axis, and one whose overlap-gate envelope factors are themselves such gathers, gather exactly the same way (CONFORMANCE_SPEC §5.5.7). OPTIONAL, and only meaningful when kind is "derived" and `from_faq` names an overlap-gated `distinct` aggregate. Pairs with a provider axis marked `gated_by: "<this set>"`, which is DEFERRED past value-invention and then fetched with a Selection built from the materialised members in sorted order (the `gated_select` pushdown).
619
+ * derived: name of the buffer that receives the surviving member key per invented position (CONFORMANCE_SPEC §5.5, Hook 1) — fed back as a `const` factor so a `faq` ranging over the compact derived axis can gather the full-grid rows that axis selects. That is what downstream FAQ nodes do; it is not a claim about ORIENTATION — a `faq` that reduces OVER the compact axis, and one whose overlap-gate envelope factors are themselves such gathers, gather exactly the same way (CONFORMANCE_SPEC §5.5.7). OPTIONAL, and only meaningful when kind is "derived" and `from_faq` names an overlap-gated `distinct` `faq`. Pairs with a provider axis marked `gated_by: "<this set>"`, which is DEFERRED past value-invention and then fetched with a Selection built from the materialised members in sorted order (the `gated_select` pushdown).
608
620
  */
609
621
  member_factor?: string;
610
622
  /**
@@ -612,11 +624,11 @@ type IndexSet = IndexSet1 & {
612
624
  */
613
625
  of?: string[];
614
626
  /**
615
- * ragged: name of the keyed factor giving |set(i)| for each parent tuple — the per-parent length / CSR offsets (e.g. MPAS nEdgesOnCell). Required when kind is "ragged".
627
+ * ragged: name of the keyed factor giving |set(i)| for each parent tuple — the per-parent LENGTH, not a cumulative offset (e.g. MPAS nEdgesOnCell). A `faq` range {"from": <this set>, "of": ["i"]} binds the POSITION k in 1…offsets[i] (esm-spec §4.3.1 "Ragged ranges"). Required when kind is "ragged".
616
628
  */
617
629
  offsets?: string;
618
630
  /**
619
- * ragged: name of the keyed factor giving the member at (i, k) for k in 1…|set(i)| — the flattened CSR member array (e.g. edgesOnCell). Required when kind is "ragged".
631
+ * ragged: name of the keyed factor giving the member at (i, k) for k in 1…|set(i)| — a PADDED [parent, max length] array whose row i holds parent i's members in positions 1…offsets[i] (e.g. MPAS edgesOnCell); entries past offsets[i] are padding and are never read. Because a range over the set binds the position k, a `faq` body reads a member by gathering it explicitly, index(values, i, k); a body that never reads this array is `ragged_values_not_gathered`. The one exception is a value-invention `faq` (distinct / skolem / rank / argmin / argmax), whose ragged range binds the member values[i, k] itself (esm-spec §4.3.1 "Ragged ranges"). Required when kind is "ragged".
620
632
  */
621
633
  values?: string;
622
634
  };
@@ -678,7 +690,7 @@ interface ESMFormat2 {
678
690
  [k: string]: DataSource;
679
691
  };
680
692
  /**
681
- * File-local symbol-to-positive-integer mappings used by the 'enum' AST op to make categorical lookups cross-binding-portable. Each entry is an enum name; its value is an object mapping symbolic names (strings) to positive integers. Two .esm files may declare an enum of the same name with different mappings; enums are file-local and never merged across files. See esm-spec.md §9.3.
693
+ * File-local symbol-to-integer mappings used by the 'enum' AST op to make categorical lookups cross-binding-portable. Each entry is an enum name; its value is an object mapping symbolic names (strings) to integers — any integer, negative, zero or positive, since an enum member is a categorical code rather than a 1-based position. Two .esm files may declare an enum of the same name with different mappings; enums are file-local and never merged across files. See esm-spec.md §9.3.
682
694
  */
683
695
  enums?: {
684
696
  [k: string]: EnumDeclaration;
@@ -695,7 +707,7 @@ interface ESMFormat2 {
695
707
  [k: string]: FunctionTable;
696
708
  };
697
709
  /**
698
- * Document-scoped registry of named index sets (RFC semiring-faq-unified-ir §5.2), keyed by name — the single, document-level declaration site for every iteration domain (grid axes, categorical dimensions, data-derived sets) shared by all models in the document. An `aggregate` range references one by name as { "from": <name> }. Each entry is an interval (dense axis), categorical enumeration, data-derived set, or ragged / dependent inner set. A reference resolves by name to exactly one entry; resolvers MUST error on an undeclared name.
710
+ * Document-scoped registry of named index sets (RFC semiring-faq-unified-ir §5.2), keyed by name — the single, document-level declaration site for every iteration domain (grid axes, categorical dimensions, data-derived sets) shared by all models in the document. A `faq` range references one by name as { "from": <name> }. Each entry is an interval (dense axis), categorical enumeration, data-derived set, or ragged / dependent inner set. A reference resolves by name to exactly one entry; resolvers MUST error on an undeclared name.
699
711
  */
700
712
  index_sets?: {
701
713
  [k: string]: IndexSet;
@@ -706,6 +718,7 @@ interface ESMFormat2 {
706
718
  coordinates?: {
707
719
  [k: string]: Coordinate;
708
720
  };
721
+ solver?: Solver;
709
722
  /**
710
723
  * Top-level rewrite rules / templates — the payload of a template-library file (esm-spec §9.7.1). Only valid in a library file (which carries no models/reaction_systems/data_loaders/coupling/domain, `template_import_not_library` when imported otherwise); component-local templates stay inside their model/reaction_system (§9.6.1). Arrives at esm 0.8.0 (`template_import_version_too_old`).
711
724
  */
@@ -828,7 +841,7 @@ interface Model$1 {
828
841
  * Named child subsystems, keyed by unique identifier. A subsystem is a child model, or a reference to an external file containing exactly one. Enables hierarchical model composition. Variables in subsystems are referenced via dot notation: "ParentModel.ChildModel.var". Each subsystem can be defined inline or included by reference via a local file path or URL. A `data_sources` entry is NOT a component and cannot be a subsystem: a model reaches external data through a parameter whose `update` names the source.
829
842
  */
830
843
  subsystems?: {
831
- [k: string]: Model$1 | DataSource | SubsystemRef;
844
+ [k: string]: Model$1 | SubsystemRef;
832
845
  };
833
846
  tolerance?: Tolerance;
834
847
  /**
@@ -915,136 +928,13 @@ interface ContinuousEvent {
915
928
  /**
916
929
  * Root-finding direction.
917
930
  */
918
- root_find?: "left" | "right" | "all";
931
+ root_find?: "left" | "right";
919
932
  /**
920
933
  * Whether to reinitialize the system after the event.
921
934
  */
922
935
  reinitialize?: boolean;
923
936
  description?: string;
924
937
  }
925
- /**
926
- * A named external data source reduced to pure I/O: it locates, reads, decodes, slices, and filters bytes on disk. It performs no reprojection and no regridding, and — from 1.0.0 — it exposes no variables and is NOT a component. It cannot be a coupling endpoint, a subsystem, or a scoped-name path root; a model consumes it by declaring a parameter whose `update` names this source and binds one of its `file_variable`s. Grid geometry a source reads (coordinates, connectivity, metric arrays) arrives as ordinary parameters and is transformed downstream by `aggregate` FAQs and coupling expressions. Authentication and algorithm-specific tuning are runtime-only and not part of the schema.
927
- */
928
- interface DataSource {
929
- /**
930
- * Structural kind of the dataset: 'grid' (gridded array source), 'points' (scattered point/station source), or 'static' (time-invariant source). Grid geometry the source reads — coordinates, connectivity, metric arrays — arrives as ordinary parameters bound to its `file_variable`s and is consumed by `aggregate` FAQs downstream; it needs no special descriptor. Scientific role (emissions, meteorology, elevation, ...) is not schema-validated and belongs in metadata.tags.
931
- */
932
- kind: "grid" | "points" | "static";
933
- source: DataSourceLocation;
934
- temporal?: DataSourceTemporal;
935
- determinism?: DataSourceDeterminism;
936
- /**
937
- * Format-specific DECODE options, passed through to the format reader verbatim (EarthSciIO calls them `reader_kwargs`): the zip `member_glob` and `skip_header_row` of an FF10 inventory, a GeoTIFF band naming, and so on. They say how bytes become an array, never what the array means — no remap, no unit conversion, no filtering (those are `variables`, `unit_conversion` and `record_filter`). A key the bound reader does not recognise MUST be an error, so a mis-spelled option cannot silently decode something else.
938
- */
939
- reader_options?: {
940
- [k: string]: unknown;
941
- };
942
- select?: DataSourceSelect;
943
- record_filter?: DataSourceRecordFilter;
944
- extent?: DataSourceExtent;
945
- reference?: Reference;
946
- /**
947
- * Free-form metadata about the data source. The "tags" field (array of strings) is conventional for expressing scientific role (e.g. "emissions", "reanalysis") and is not schema-validated.
948
- */
949
- metadata?: {
950
- tags?: string[];
951
- [k: string]: unknown;
952
- };
953
- }
954
- /**
955
- * File discovery configuration. Describes how to locate data files at runtime via URL templates with date/variable substitutions.
956
- */
957
- interface DataSourceLocation {
958
- /**
959
- * Jinja-style URL template with substitutions. Supported: {date:<strftime>} (e.g. {date:%Y%m%d}), {var}, {sector}, {species}. Custom substitutions are allowed and the runtime must accept and pass them through.
960
- */
961
- url_template: string;
962
- /**
963
- * Ordered fallback URL templates. Runtime tries each in order, first is primary. Follows the same substitution grammar as url_template.
964
- */
965
- mirrors?: string[];
966
- }
967
- /**
968
- * Temporal coverage and record layout for a data source.
969
- */
970
- interface DataSourceTemporal {
971
- /**
972
- * ISO 8601 datetime — first timestamp available from this source.
973
- */
974
- start?: string;
975
- /**
976
- * ISO 8601 datetime — last timestamp available from this source.
977
- */
978
- end?: string;
979
- /**
980
- * ISO 8601 duration describing how much time one file covers (e.g., "P1D", "P1M", "PT3H").
981
- */
982
- file_period?: string;
983
- /**
984
- * ISO 8601 duration describing spacing between samples within a file.
985
- */
986
- frequency?: string;
987
- /**
988
- * Number of time records per file. "auto" means read from file at runtime.
989
- */
990
- records_per_file?: number | "auto";
991
- /**
992
- * Name of the time coordinate variable in the file. Used when records_per_file is absent or "auto". If both static declarations (records_per_file + frequency) and time_variable are present, the static declaration wins and time_variable is a fallback.
993
- */
994
- time_variable?: string;
995
- /**
996
- * How many time records the source returns per query time — pure I/O; the source does NOT interpolate. Absent or 1 (default): the single at-or-before record, time axis dropped (piecewise-constant between ticks). 2: the two records bracketing the current time (floor + successor), returned with the time axis kept at length 2 so a downstream model can interpolate in time (e.g. linearly). Only 1 and 2 are supported; higher-order temporal stencils are future work. Contrast records_per_file (records IN one file).
997
- */
998
- records_per_sample?: 1 | 2;
999
- }
1000
- /**
1001
- * Reproducibility contract a binary-format loader advertises to bindings. A binding that cannot honor the declared endian / float_format / integer_width MUST reject the file at load rather than silently reinterpreting bytes.
1002
- */
1003
- interface DataSourceDeterminism {
1004
- /**
1005
- * Byte order of on-wire numeric fields.
1006
- */
1007
- endian?: "little" | "big";
1008
- /**
1009
- * Floating-point format of metric fields.
1010
- */
1011
- float_format?: "ieee754_single" | "ieee754_double";
1012
- /**
1013
- * Integer width (in bits) of connectivity fields.
1014
- */
1015
- integer_width?: 32 | 64;
1016
- }
1017
- /**
1018
- * A per-axis selection of what the source DELIVERS from an on-disk array — one entry per NATIVE array dimension, in native dims order. Declared on a source (the default for every parameter drawing from it) or on a single parameter's `from` binding (which overrides the source's). Two parameters of one source MAY read the same `file_variable` under different selections, which is how a full-grid field and a prefix of it are both declared instead of one being sliced by the caller. The selection is defined over the axis the loader DELIVERS, so it follows any `record_filter`: `range 0..200` on a filtered points table is the first 200 SURVIVING records. Whether a binding pushes it down to the reader (fetching only what it keeps) or applies it after the read is an optimization; the two MUST agree exactly.
1019
- */
1020
- interface DataSourceSelect {
1021
- /**
1022
- * One selector per native array dimension, in native dims order.
1023
- *
1024
- * @minItems 1
1025
- */
1026
- axes: [DataSourceSelectAxis, ...DataSourceSelectAxis[]];
1027
- }
1028
- /**
1029
- * Which records of a `points` source are DELIVERED. The surviving mask is computed ONCE for the source and applied to every parameter drawing from it, so its columns can never fall out of alignment, and the surviving count is the source's `extent`. A record is dropped when any `require_finite` variable is non-finite at it, or when a `codes` map with `unmapped: "drop"` does not recognise its value.
1030
- */
1031
- interface DataSourceRecordFilter {
1032
- /**
1033
- * `file_variable` names whose value must be finite for a record to survive. (Pre-1.0.0 this named loader variable names; a source has no variables of its own now, so it names the file's.) A point with no coordinate cannot be placed and a row with no annual total cannot be weighted; dropping it is a declaration about the source, rather than a NaN travelling into the model to surface as an empty result later.
1034
- *
1035
- * @minItems 1
1036
- */
1037
- require_finite?: [string, ...string[]];
1038
- }
1039
- /**
1040
- * Binds the source's DELIVERED record count to a metaparameter, for a source whose extent is not knowable until it is read. A binding samples such a source BEFORE it closes metaparameters (esm-spec §9.7.6 site 4), so an index set declared `size: "N_REC"` is sized by the data itself and no caller counts rows and passes the number in. Every parameter drawing from the source MUST agree on the count — that agreement is also the alignment check.
1041
- */
1042
- interface DataSourceExtent {
1043
- /**
1044
- * Name of the `metaparameters` entry it binds. Declare that entry with a `default` (conventionally 0) so the document still validates and loads standalone.
1045
- */
1046
- metaparameter: string;
1047
- }
1048
938
  /**
1049
939
  * A reference to an external ESM file containing a model or reaction system definition. The ref field can be a relative or absolute local file path, or an HTTP/HTTPS URL. Relative paths are resolved relative to the directory of the referencing file.
1050
940
  */
@@ -1071,6 +961,12 @@ interface SubsystemRef {
1071
961
  * Template-library imports registered into the REFERENCED component's template scope (esm-spec §9.7.10) — assembler-chosen discretization for a mounted PDE component, without editing the leaf file. Same entry shape as §9.7.2; target implicit (this edge mounts one component). Load-time only; consumed by the §9.6.3 fixpoint; does not survive parse→emit.
1072
962
  */
1073
963
  expression_template_imports?: TemplateImport[];
964
+ /**
965
+ * Mount-edge index-set renaming (esm-spec §4.7 'Mount-edge index-set renaming'; docs/content/rfcs/mount-edge-index-set-renaming.md): a map from an index-set name AS THE RESOLVED MOUNTED DOCUMENT SPELLS IT to the name it takes in the mounting document. `index_sets` is a document-scoped registry, so two mounts that independently use one axis name at different lengths (a 59-layer atmospheric column and a 4-layer soil column both over `lev`) collide with subsystem_index_set_conflict; this field is the §9.7.7 renaming mechanism at a component-mount edge, restricted to index sets because they are the only declaration kind a mount contributes to document scope. Applied as ONE simultaneous substitution AFTER the referenced document resolves completely (its own imports, this edge's `bindings` and injection, its metaparameter close and the §9.6.3 fixpoint) and BEFORE its `index_sets` merge, transitively through every occurrence inside the mounted document — `index_sets` keys and ragged `of` lists, `{"from": …}` ranges, the `wrt`/`dim`/`var` axis scalars and bare-axis-name `integral` bounds, `where` `shape` constraints, variable/parameter `shape` lists, `Assertion.coords` keys, and `DataSourceSelectAxis.gated_by`. Keys must name an index set of the resolved mounted document (subsystem_index_set_rename_unknown_name); targets are dotted identifiers (template_import_rename_invalid) and must be distinct (template_import_rename_collision). The map need not be total: an unnamed axis passes through unrenamed, so a deliberately shared axis still merges deep-equal. Load-time only; consumed at the mount; does not survive parse→emit.
966
+ */
967
+ index_set_rename?: {
968
+ [k: string]: string;
969
+ };
1074
970
  }
1075
971
  /**
1076
972
  * One entry of `expression_template_imports` (esm-spec §9.7.2): imports the templates (and index_sets / open metaparameters) of a template-library file. `ref` uses the §4.7 reference formats (relative path, absolute path, or URL), resolved at load before validation with canonical-path cycle detection (`template_import_cycle`). `only` filters which template names become visible to the importer (`template_import_unknown_name` for unknown names). `bindings` closes the target document's open metaparameters to integers at this edge (esm-spec §9.7.6); metaparameters left unbound are re-exported into the importing document's scope. `prefix` / `rename` namespace the surviving exported names (templates after `only`, index sets, still-open metaparameters) into the importer's vocabulary, applied transitively through every occurrence inside the imported declarations; `rebind` rewrites free variable names (keyed factors and other free names in template bodies) at the same point (esm-spec §9.7.7). Renaming happens after this edge's `bindings` instantiation and `only` filtering and before the §9.7.4/§9.7.5 merge, so the same file imported under different renames registers as distinct instances while identical edges dedupe. Identifier grammar, unknown-name, and collision checks are resolver-level (`template_import_rename_invalid`, `template_import_rename_unknown_name`, `template_import_rebind_unknown_name`, `template_import_rename_collision`).
@@ -1110,7 +1006,7 @@ interface TemplateImport {
1110
1006
  };
1111
1007
  }
1112
1008
  /**
1113
- * Model-level default numerical tolerance for tests, used when a test or assertion does not provide its own.
1009
+ * Model-level default numerical tolerance for tests. Resolution is PER FIELD over four levels (esm-spec 6.6.4): assertion, test, model, then the implementation default (rel = 1e-6, no abs bound). abs and rel each take their value from the innermost level that declares that field, so a test or assertion that declares only one bound does not mask the other from here. An explicit 0 is a declaration ('no bound of this kind'), not an absence, and stops the fallthrough - including the fallthrough to the implementation default, which is why rel: 0 is the only way to ask for an absolute-only comparison.
1114
1010
  */
1115
1011
  interface Tolerance {
1116
1012
  /**
@@ -1118,7 +1014,7 @@ interface Tolerance {
1118
1014
  */
1119
1015
  abs?: number;
1120
1016
  /**
1121
- * Relative tolerance: |actual - expected| / max(|expected|, epsilon) <= rel.
1017
+ * Relative tolerance: |actual - expected| <= rel * max(|actual|, |expected|). Symmetric in actual and expected -- the scale is the larger of the two magnitudes, not |expected| alone.
1122
1018
  */
1123
1019
  rel?: number;
1124
1020
  }
@@ -1135,16 +1031,22 @@ interface Test {
1135
1031
  */
1136
1032
  description?: string;
1137
1033
  /**
1138
- * Initial-value overrides for unknowns, keyed by variable name (local to this component). Values not listed fall back to the variable's declared default.
1034
+ * Initial-value overrides for unknowns, keyed by variable name (local to this component). Values not listed fall back to the variable's declared default. A shaped unknown's value may be a row-major nested JSON array matching its declared shape (esm-spec §6.6.2).
1139
1035
  */
1140
1036
  initial_conditions?: {
1141
- [k: string]: number;
1037
+ /**
1038
+ * Initial value for one unknown: a number, or — for a SHAPED unknown — a row-major nested JSON array matching its declared shape after metaparameter folding (a mismatch is a load-time error). A scalar broadcasts to every element (esm-spec §6.6.2).
1039
+ */
1040
+ [k: string]: number | NumericArrayLiteral;
1142
1041
  };
1143
1042
  /**
1144
- * Parameter overrides, keyed by parameter name (local to this component). Values not listed fall back to the parameter's declared default.
1043
+ * Parameter overrides, keyed by parameter name (local to this component). Values not listed fall back to the parameter's declared default. A shaped parameter's value may be a row-major nested JSON array matching its declared shape (esm-spec §6.6.2).
1145
1044
  */
1146
1045
  parameter_overrides?: {
1147
- [k: string]: number;
1046
+ /**
1047
+ * Value for one parameter: a number, or — for a SHAPED parameter — a row-major nested JSON array matching its declared shape after metaparameter folding (a mismatch is a load-time error). A scalar broadcasts to every element (esm-spec §6.6.2).
1048
+ */
1049
+ [k: string]: number | NumericArrayLiteral;
1148
1050
  };
1149
1051
  time_span: TimeSpan;
1150
1052
  tolerance?: Tolerance1;
@@ -1167,7 +1069,7 @@ interface TimeSpan {
1167
1069
  end: number;
1168
1070
  }
1169
1071
  /**
1170
- * Test-level default tolerance applied to all assertions in this test that do not override it.
1072
+ * Test-level default tolerance, merged PER FIELD (esm-spec 6.6.4): it supplies each of abs and rel that an assertion in this test does not declare, and falls through to the model-level default - and then to the implementation default rel = 1e-6 - for each field it does not declare itself. Write rel: 0 to stop that fallthrough.
1171
1073
  */
1172
1074
  interface Tolerance1 {
1173
1075
  /**
@@ -1175,12 +1077,12 @@ interface Tolerance1 {
1175
1077
  */
1176
1078
  abs?: number;
1177
1079
  /**
1178
- * Relative tolerance: |actual - expected| / max(|expected|, epsilon) <= rel.
1080
+ * Relative tolerance: |actual - expected| <= rel * max(|actual|, |expected|). Symmetric in actual and expected -- the scale is the larger of the two magnitudes, not |expected| alone.
1179
1081
  */
1180
1082
  rel?: number;
1181
1083
  }
1182
1084
  /**
1183
- * Per-assertion tolerance override. If present, this takes precedence over the test-level and model-level defaults.
1085
+ * Per-assertion tolerance override, merged PER FIELD over the test-level and model-level defaults and then the implementation default (esm-spec 6.6.4): each of abs and rel that this object declares wins, and each it omits falls through independently. Declaring only abs here therefore keeps an outer rel rather than dropping it, and falls through to the implementation default rel = 1e-6 when no enclosing level declares one; write rel: 0 to mean 'no relative bound'.
1184
1086
  */
1185
1087
  interface Tolerance2 {
1186
1088
  /**
@@ -1188,7 +1090,7 @@ interface Tolerance2 {
1188
1090
  */
1189
1091
  abs?: number;
1190
1092
  /**
1191
- * Relative tolerance: |actual - expected| / max(|expected|, epsilon) <= rel.
1093
+ * Relative tolerance: |actual - expected| <= rel * max(|actual|, |expected|). Symmetric in actual and expected -- the scale is the larger of the two magnitudes, not |expected| alone.
1192
1094
  */
1193
1095
  rel?: number;
1194
1096
  }
@@ -1298,7 +1200,7 @@ interface PlotSeries {
1298
1200
  variable: string;
1299
1201
  }
1300
1202
  /**
1301
- * A single in-file rewrite rule / Expression-AST template (esm-spec §9.6 / docs/rfcs/ast-expression-templates.md). The `params` are metavariables; the `body` is the replacement Expression AST in which parameter occurrences are written as bare parameter-name strings. The template is applied in one of two ways. (1) WITHOUT `match`: it is invoked explicitly by name through an `apply_expression_template` node, whose `bindings` supply each parameter's AST. (2) WITH `match`: it is an auto-applied rewrite rule — `match` is a pattern Expression in which parameters are wildcards (a parameter in an operand/`args` position binds to the matched sub-AST; a parameter in a scalar field such as `dim`/`side` binds to the matched literal), and the rule fires wherever the pattern structurally matches a node. This unifies variable substitution (a bare-metavar `match`), named-template expansion (no `match`), and rewrite-target-op lowering (an operator `match` like `{op:'D', args:['f'], wrt:'x'}` → an `aggregate`/`makearray` stencil). Either way the `body` is instantiated by pure structural substitution of the bound metavariables — no evaluation, no metaprogramming. Rewriting is an outermost-first, priority-ordered, bounded-fixpoint load-time process (esm-spec §9.6.3): each pass is a pre-order walk that fires, at each node, the matching rule of highest `priority` (ties broken by declaration order); passes repeat to a fixpoint or until MAX_REWRITE_PASSES=64, at which point a non-converging rule set is rejected with diagnostic 'rewrite_rule_nonterminating'. A rewrite-target op (esm-spec §4.2) surviving the fixpoint into an evaluation position is rejected with diagnostic 'unlowered_operator'. A `body` MAY contain `apply_expression_template` nodes referencing other match-less in-scope templates (declared locally or imported, esm-spec §9.7.2); these are checked at registration time as a statically-checked acyclic DAG (cycles rejected with 'apply_expression_template_recursive_body'; chains deeper than MAX_TEMPLATE_EXPANSION_DEPTH=32 rejected with 'template_body_expansion_too_deep'); from esm 0.9.0 they are preserved uninlined and denote their expansion (Option B, esm-spec §9.6.4; pre-0.9.0 loaders inlined them here by pure substitution). `match` patterns MUST NOT contain `apply_expression_template` nodes. An optional `where` block adds static match-scoping constraints on the captured parameters (declared-shape/index-set scoping, filtered before priority selection — see the `where` property and esm-spec §9.6.1).
1203
+ * A single in-file rewrite rule / Expression-AST template (esm-spec §9.6 / docs/rfcs/ast-expression-templates.md). The `params` are metavariables; the `body` is the replacement Expression AST in which parameter occurrences are written as bare parameter-name strings. The template is applied in one of two ways. (1) WITHOUT `match`: it is invoked explicitly by name through an `apply_expression_template` node, whose `bindings` supply each parameter's AST. (2) WITH `match`: it is an auto-applied rewrite rule — `match` is a pattern Expression in which parameters are wildcards (a parameter in an operand/`args` position binds to the matched sub-AST; a parameter in a scalar field such as `dim`/`side` binds to the matched literal), and the rule fires wherever the pattern structurally matches a node. This unifies variable substitution (a bare-metavar `match`), named-template expansion (no `match`), and rewrite-target-op lowering (an operator `match` like `{op:'D', args:['f'], wrt:'x'}` → a `faq`/`makearray` stencil). Either way the `body` is instantiated by pure structural substitution of the bound metavariables — no evaluation, no metaprogramming. Rewriting is an outermost-first, priority-ordered, bounded-fixpoint load-time process (esm-spec §9.6.3): each pass is a pre-order walk that fires, at each node, the matching rule of highest `priority` (ties broken by declaration order); passes repeat to a fixpoint or until MAX_REWRITE_PASSES=64, at which point a non-converging rule set is rejected with diagnostic 'rewrite_rule_nonterminating'. A rewrite-target op (esm-spec §4.2) surviving the fixpoint into an evaluation position is rejected with diagnostic 'unlowered_operator'. A `body` MAY contain `apply_expression_template` nodes referencing other match-less in-scope templates (declared locally or imported, esm-spec §9.7.2); these are checked at registration time as a statically-checked acyclic DAG (cycles rejected with 'apply_expression_template_recursive_body'; chains deeper than MAX_TEMPLATE_EXPANSION_DEPTH=32 rejected with 'template_body_expansion_too_deep'); from esm 0.9.0 they are preserved uninlined and denote their expansion (Option B, esm-spec §9.6.4; pre-0.9.0 loaders inlined them here by pure substitution). `match` patterns MUST NOT contain `apply_expression_template` nodes. An optional `where` block adds static match-scoping constraints on the captured parameters (declared-shape/index-set scoping, filtered before priority selection — see the `where` property and esm-spec §9.6.1).
1302
1204
  */
1303
1205
  interface ExpressionTemplate {
1304
1206
  /**
@@ -1434,7 +1336,7 @@ interface StoichiometryEntry {
1434
1336
  stoichiometry: number;
1435
1337
  }
1436
1338
  /**
1437
- * System-level default numerical tolerance for tests, used when a test or assertion does not provide its own.
1339
+ * System-level default numerical tolerance for tests. Resolution is PER FIELD over four levels (esm-spec 6.6.4): assertion, test, system, then the implementation default (rel = 1e-6, no abs bound). abs and rel each take their value from the innermost level that declares that field, so a test or assertion that declares only one bound does not mask the other from here. An explicit 0 is a declaration ('no bound of this kind'), not an absence, and stops the fallthrough - including the fallthrough to the implementation default, which is why rel: 0 is the only way to ask for an absolute-only comparison.
1438
1340
  */
1439
1341
  interface Tolerance3 {
1440
1342
  /**
@@ -1442,12 +1344,135 @@ interface Tolerance3 {
1442
1344
  */
1443
1345
  abs?: number;
1444
1346
  /**
1445
- * Relative tolerance: |actual - expected| / max(|expected|, epsilon) <= rel.
1347
+ * Relative tolerance: |actual - expected| <= rel * max(|actual|, |expected|). Symmetric in actual and expected -- the scale is the larger of the two magnitudes, not |expected| alone.
1446
1348
  */
1447
1349
  rel?: number;
1448
1350
  }
1449
1351
  /**
1450
- * A file-local enum mapping symbolic names to positive integers (esm-spec.md §9.3). Within a single enum, integer values MUST be unique. Across enums, values MAY collide (each enum is its own namespace). Bindings resolve enum-op nodes at load time before evaluating expressions.
1352
+ * A named external data source reduced to pure I/O: it locates, reads, decodes, slices, and filters bytes on disk. It performs no reprojection and no regridding, and — from 1.0.0 — it exposes no variables and is NOT a component. It cannot be a coupling endpoint, a subsystem, or a scoped-name path root; a model consumes it by declaring a parameter whose `update` names this source and binds one of its `file_variable`s. Grid geometry a source reads (coordinates, connectivity, metric arrays) arrives as ordinary parameters and is transformed downstream by `faq` nodes and coupling expressions. Authentication and algorithm-specific tuning are runtime-only and not part of the schema.
1353
+ */
1354
+ interface DataSource {
1355
+ /**
1356
+ * Structural kind of the dataset: 'grid' (gridded array source), 'points' (scattered point/station source), or 'static' (time-invariant source). Grid geometry the source reads — coordinates, connectivity, metric arrays — arrives as ordinary parameters bound to its `file_variable`s and is consumed by `faq` nodes downstream; it needs no special descriptor. Scientific role (emissions, meteorology, elevation, ...) is not schema-validated and belongs in metadata.tags.
1357
+ */
1358
+ kind: "grid" | "points" | "static";
1359
+ source: DataSourceLocation;
1360
+ temporal?: DataSourceTemporal;
1361
+ determinism?: DataSourceDeterminism;
1362
+ /**
1363
+ * Format-specific DECODE options, passed through to the format reader verbatim (EarthSciIO calls them `reader_kwargs`): the zip `member_glob` and `skip_header_row` of an FF10 inventory, a GeoTIFF band naming, and so on. They say how bytes become an array, never what the array means — no remap, no unit conversion, no filtering (those are `variables`, `unit_conversion` and `record_filter`). A key the bound reader does not recognise MUST be an error, so a mis-spelled option cannot silently decode something else.
1364
+ */
1365
+ reader_options?: {
1366
+ [k: string]: unknown;
1367
+ };
1368
+ select?: DataSourceSelect;
1369
+ record_filter?: DataSourceRecordFilter;
1370
+ extent?: DataSourceExtent;
1371
+ reference?: Reference;
1372
+ /**
1373
+ * Free-form metadata about the data source. The "tags" field (array of strings) is conventional for expressing scientific role (e.g. "emissions", "reanalysis") and is not schema-validated.
1374
+ */
1375
+ metadata?: {
1376
+ tags?: string[];
1377
+ [k: string]: unknown;
1378
+ };
1379
+ }
1380
+ /**
1381
+ * File discovery configuration. Describes how to locate data files at runtime via URL templates with date/variable substitutions.
1382
+ */
1383
+ interface DataSourceLocation {
1384
+ /**
1385
+ * Jinja-style URL template with substitutions. Supported: {date:<strftime>} (e.g. {date:%Y%m%d}), {var}, {sector}, {species}. Custom substitutions are allowed and the runtime must accept and pass them through.
1386
+ */
1387
+ url_template: string;
1388
+ /**
1389
+ * Ordered fallback URL templates. Runtime tries each in order, first is primary. Follows the same substitution grammar as url_template.
1390
+ */
1391
+ mirrors?: string[];
1392
+ }
1393
+ /**
1394
+ * Temporal coverage and record layout for a data source.
1395
+ */
1396
+ interface DataSourceTemporal {
1397
+ /**
1398
+ * ISO 8601 datetime — first timestamp available from this source.
1399
+ */
1400
+ start?: string;
1401
+ /**
1402
+ * ISO 8601 datetime — last timestamp available from this source.
1403
+ */
1404
+ end?: string;
1405
+ /**
1406
+ * ISO 8601 duration describing how much time one file covers (e.g., "P1D", "P1M", "PT3H").
1407
+ */
1408
+ file_period?: string;
1409
+ /**
1410
+ * ISO 8601 duration describing spacing between samples within a file.
1411
+ */
1412
+ frequency?: string;
1413
+ /**
1414
+ * Number of time records per file. "auto" means read from file at runtime.
1415
+ */
1416
+ records_per_file?: number | "auto";
1417
+ /**
1418
+ * Name of the time coordinate variable in the file. Used when records_per_file is absent or "auto". If both static declarations (records_per_file + frequency) and time_variable are present, the static declaration wins and time_variable is a fallback.
1419
+ */
1420
+ time_variable?: string;
1421
+ /**
1422
+ * How many time records the source returns per query time — pure I/O; the source does NOT interpolate. Absent or 1 (default): the single at-or-before record, time axis dropped (piecewise-constant between ticks). 2: the two records bracketing the current time (floor + successor), returned with the time axis kept at length 2 so a downstream model can interpolate in time (e.g. linearly). Only 1 and 2 are supported; higher-order temporal stencils are future work. Contrast records_per_file (records IN one file).
1423
+ */
1424
+ records_per_sample?: 1 | 2;
1425
+ }
1426
+ /**
1427
+ * Reproducibility contract a binary-format loader advertises to bindings. A binding that cannot honor the declared endian / float_format / integer_width MUST reject the file at load rather than silently reinterpreting bytes.
1428
+ */
1429
+ interface DataSourceDeterminism {
1430
+ /**
1431
+ * Byte order of on-wire numeric fields.
1432
+ */
1433
+ endian?: "little" | "big";
1434
+ /**
1435
+ * Floating-point format of metric fields.
1436
+ */
1437
+ float_format?: "ieee754_single" | "ieee754_double";
1438
+ /**
1439
+ * Integer width (in bits) of connectivity fields.
1440
+ */
1441
+ integer_width?: 32 | 64;
1442
+ }
1443
+ /**
1444
+ * A per-axis selection of what the source DELIVERS from an on-disk array — one entry per NATIVE array dimension, in native dims order. Declared on a source (the default for every parameter drawing from it) or on a single parameter's `from` binding (which overrides the source's). Two parameters of one source MAY read the same `file_variable` under different selections, which is how a full-grid field and a prefix of it are both declared instead of one being sliced by the caller. The selection is defined over the axis the loader DELIVERS, so it follows any `record_filter`: `range 0..200` on a filtered points table is the first 200 SURVIVING records. Whether a binding pushes it down to the reader (fetching only what it keeps) or applies it after the read is an optimization; the two MUST agree exactly.
1445
+ */
1446
+ interface DataSourceSelect {
1447
+ /**
1448
+ * One selector per native array dimension, in native dims order.
1449
+ *
1450
+ * @minItems 1
1451
+ */
1452
+ axes: [DataSourceSelectAxis, ...DataSourceSelectAxis[]];
1453
+ }
1454
+ /**
1455
+ * Which records of a `points` source are DELIVERED. The surviving mask is computed ONCE for the source and applied to every parameter drawing from it, so its columns can never fall out of alignment, and the surviving count is the source's `extent`. A record is dropped when any `require_finite` variable is non-finite at it, or when a `codes` map with `unmapped: "drop"` does not recognise its value.
1456
+ */
1457
+ interface DataSourceRecordFilter {
1458
+ /**
1459
+ * `file_variable` names whose value must be finite for a record to survive. (Pre-1.0.0 this named loader variable names; a source has no variables of its own now, so it names the file's.) A point with no coordinate cannot be placed and a row with no annual total cannot be weighted; dropping it is a declaration about the source, rather than a NaN travelling into the model to surface as an empty result later.
1460
+ *
1461
+ * @minItems 1
1462
+ */
1463
+ require_finite?: [string, ...string[]];
1464
+ }
1465
+ /**
1466
+ * Binds the source's DELIVERED record count to a metaparameter, for a source whose extent is not knowable until it is read. A binding samples such a source BEFORE it closes metaparameters (esm-spec §9.7.6 site 4), so an index set declared `size: "N_REC"` is sized by the data itself and no caller counts rows and passes the number in. Every parameter drawing from the source MUST agree on the count — that agreement is also the alignment check.
1467
+ */
1468
+ interface DataSourceExtent {
1469
+ /**
1470
+ * Name of the `metaparameters` entry it binds. Declare that entry with a `default` (conventionally 0) so the document still validates and loads standalone.
1471
+ */
1472
+ metaparameter: string;
1473
+ }
1474
+ /**
1475
+ * A file-local enum mapping symbolic names to integers (esm-spec.md §9.3). The value domain is the WHOLE integer range — negative, zero and positive — because an enum member is a categorical CODE, not a position: it lowers to a `const` NUMBER used in arithmetic and in `join.on` key comparisons, where 0 and -1 are ordinary values (a source table's `opModeID = 0` or `polProcessID = -1` is a real code, not an absence). The two 1-based constructs in the format — `index`-op / index-set coordinates and `makearray` regions — are separate and carry their own bounds validation; using an enum member as an `index` argument is an authoring choice the index op bounds-checks, not a constraint on this block. Within a single enum, integer values MUST be unique. Across enums, values MAY collide (each enum is its own namespace). Bindings resolve enum-op nodes at load time before evaluating expressions.
1451
1476
  */
1452
1477
  interface EnumDeclaration {
1453
1478
  [k: string]: number;
@@ -1470,6 +1495,10 @@ interface CouplingOperatorCompose {
1470
1495
  translate?: {
1471
1496
  [k: string]: TranslateTarget;
1472
1497
  };
1498
+ /**
1499
+ * The entry's MERGE INTENT (esm-libraries-spec §4.7.1 step 5). TRI-STATE: absent is NOT the same as `false`, which is why this property declares no default. ABSENT — the author has not said; an entry that merges NOTHING is then `operator_compose_no_merge`, a hard refusal at flatten (such an entry is indistinguishable from one that is not there: the operator would integrate a private decoupled system from its own defaults and the other system would receive no contribution), while a PARTIAL merge is a warning. `true` — the `systems[1]` equations are CONTRIBUTIONS and every one of them whose LHS names a dependent variable MUST match an equation of `systems[0]`; any shortfall, PARTIAL included, is `operator_compose_require_match_unmatched`. There is no 'some is enough' reading. `false` — a standalone-contributing operator, DECLARED: unmatched equations are expected, are preserved per step 5, and nothing is reported.
1500
+ */
1501
+ require_match?: boolean;
1473
1502
  /**
1474
1503
  * Strategy for mapping between 0D and spatial systems.
1475
1504
  */
@@ -1653,7 +1682,28 @@ interface FunctionTableAxis {
1653
1682
  values: [number, number, ...number[]];
1654
1683
  }
1655
1684
  /**
1656
- * Document-scoped named integers bound at load (esm-spec §9.7.6): at import/subsystem edges via `bindings`, at the loader API for the root document, or by `default`. Admissible — as names or `{op, args}` integer expressions — in `index_sets` interval sizes, `aggregate` dense ranges, and `makearray` regions (folded exactly at load), and substituted as integer literals in ordinary expression positions. A metaparameter name MUST NOT collide with any visible variable/parameter/species/index-set name (`metaparameter_name_conflict`).
1685
+ * Document-scoped, OPTIONAL solver hints (esm-spec §2.2): numerics the document knows about ITSELF, which each binding maps to its own integrator. Purely additive — a document without it validates, flattens and emits exactly as before. Every field is ADVISORY: a binding MAY ignore any or all of them and still conform. Advisory governs the MECHANISM, never the OUTCOME — a binding that ignores every field and still converges conforms; the requirement to integrate successfully and agree within the CONFORMANCE_SPEC §5.9 error band is untouched by this block and is not excused by it. What IS normative: parse it, validate it, round-trip it VERBATIM (it is authored configuration, a peer of `tolerance` and `parameter_overrides` — not a load-time construct like `expression_template_imports`), leave the flattened system unchanged, and reject it in a document declaring `esm` below 1.1.0 with `solver_version_too_old`. This block is NOT for algorithm names (`BDF`/`LSODA`/`Rosenbrock23` are per-binding identifiers and would not be portable — there is deliberately no `alg` field), NOT for binding-specific compile knobs (`cse` is a sympy.lambdify concern), and NOT a DAE declaration (`system_class` is DERIVED from the equation set). An empty block (`"solver": {}`) is LEGAL and means exactly what absence means; it NORMALIZES to absence at load, so it does not survive `parse -> emit` and the typed value never holds a block with nothing set. That normalization is why the schema does NOT carry `minProperties: 1`: every other optional top-level container (`coordinates`, `index_sets`, `metaparameters`, `coupling_roles`) admits an empty object, and a lone exception here would be a rule a reader has to learn for no gain. Arrives at esm 1.1.0.
1686
+ */
1687
+ interface Solver {
1688
+ /**
1689
+ * The author's declaration of the system's stiffness. A binding MAY select an implicit / BDF-family integrator on `high`. ABSENCE IS NOT A DEFAULT VALUE: a document that omits this key has not declared its stiffness and does not thereby declare `low`; bindings MUST NOT read absence as an assertion about the system. The motivating case is the POLLU stiff-ODE benchmark (Verwer 1994), whose rate constants span ~8e-7 to ~7e9 1/s: scipy's LSODA cannot integrate it at all while BDF reproduces the published reference, a fact that is true of the MODEL rather than of any runner.
1690
+ */
1691
+ stiffness?: "low" | "moderate" | "high";
1692
+ /**
1693
+ * Absolute INTEGRATION tolerance the document asks for. Spelled as the `solve()` keyword (API_SPEC §4) so it passes through literally. This is a DIFFERENT QUANTITY from the `tolerance` object on a model / reaction system / test / assertion (esm-spec §6.6.4), which is the tolerance an assertion is COMPARED at; the two resolve independently and neither substitutes for the other. Resolution order, most-specific first: an explicit argument at the `solve()` call site, then this field, then the binding default (1e-6).
1694
+ */
1695
+ abstol?: number;
1696
+ /**
1697
+ * Relative INTEGRATION tolerance the document asks for. Same resolution order and the same distinction from the assertion-comparison `tolerance` object as `abstol`; the binding default is 1e-4.
1698
+ */
1699
+ reltol?: number;
1700
+ /**
1701
+ * Advisory: the system tolerates or benefits from this operator-splitting convention. Carries NO prescribed substep structure and does not amend esm-spec §9's single-flat-system model — a binding that does not split ignores it. The vocabulary is deliberately the one the discretization RFC §7.5 dimensional-splitting field already uses, so the word means one thing across the spec, even though that occurrence is executable and this one is a hint.
1702
+ */
1703
+ splitting?: "none" | "lie" | "strang";
1704
+ }
1705
+ /**
1706
+ * Document-scoped named integers bound at load (esm-spec §9.7.6): at import/subsystem edges via `bindings`, at the loader API for the root document, or by `default`. Admissible — as names or `{op, args}` integer expressions — in `index_sets` interval sizes, `faq` dense ranges, and `makearray` regions (folded exactly at load), and substituted as integer literals in ordinary expression positions. A metaparameter name MUST NOT collide with any visible variable/parameter/species/index-set name (`metaparameter_name_conflict`).
1657
1707
  */
1658
1708
  interface Metaparameters {
1659
1709
  [k: string]: {
@@ -1689,14 +1739,35 @@ declare const ERROR_CODES: {
1689
1739
  readonly CIRCULAR_DEPENDENCY: "circular_dependency";
1690
1740
  readonly DIMENSIONAL_MISMATCH: "dimensional_mismatch";
1691
1741
  readonly JOIN_KEY_INVALID_TYPE: "join_key_invalid_type";
1742
+ readonly JOIN_SIDE_AMBIGUOUS: "join_side_ambiguous";
1743
+ readonly JOIN_SYMS_UNKNOWN_SYMBOL: "join_syms_unknown_symbol";
1692
1744
  readonly DOMAIN_UNIT_MISMATCH: "domain_unit_mismatch";
1693
1745
  readonly COUPLE_MULTIPLICATIVE_NO_TENDENCY: "couple_multiplicative_no_tendency";
1746
+ readonly OPERATOR_COMPOSE_NO_MERGE: "operator_compose_no_merge";
1747
+ readonly OPERATOR_COMPOSE_PARTIAL_MERGE: "operator_compose_partial_merge";
1748
+ readonly OPERATOR_COMPOSE_REQUIRE_MATCH_UNMATCHED: "operator_compose_require_match_unmatched";
1749
+ readonly OPERATOR_COMPOSE_AMBIGUOUS_BARE_NAME: "operator_compose_ambiguous_bare_name";
1694
1750
  readonly RELATIONAL_NODE_IN_CONTINUOUS: "relational_node_in_continuous";
1751
+ readonly DERIVED_INDEX_SET_UNMATERIALIZED: "derived_index_set_unmaterialized";
1695
1752
  readonly UNDEFINED_INDEX_SET: "undefined_index_set";
1753
+ readonly RAGGED_VALUES_NOT_GATHERED: "ragged_values_not_gathered";
1696
1754
  readonly INVALID_BROADCAST_FN: "invalid_broadcast_fn";
1697
1755
  readonly ARRAY_SHAPE_MISMATCH: "array_shape_mismatch";
1756
+ readonly OBSERVED_CYCLE: "observed_cycle";
1757
+ readonly AMBIGUOUS_OUTPUT_NAME: "ambiguous_output_name";
1758
+ readonly RECURRENCE_NOT_WELLFOUNDED: "recurrence_not_wellfounded";
1759
+ readonly RECURRENCE_UNSUPPORTED_FORM: "recurrence_unsupported_form";
1760
+ readonly INDEXED_DEFINITION_UNSUPPORTED_FORM: "indexed_definition_unsupported_form";
1761
+ readonly UNSUPPORTED_CONSTRUCT: "unsupported_construct";
1762
+ readonly CALLBACK_UNREGISTERED: "callback_unregistered";
1763
+ readonly DATA_SOURCE_UNBOUND: "data_source_unbound";
1764
+ readonly COMPILER_UNKNOWN: "compiler_unknown";
1765
+ readonly COMPILER_UNAVAILABLE: "compiler_unavailable";
1766
+ readonly COMPILER_REFUSED_RULE: "compiler_refused_rule";
1698
1767
  readonly EQUATION_COUNT_MISMATCH: "equation_count_mismatch";
1699
1768
  readonly EVENT_AFFECTS_PARAMETER: "event_affects_parameter";
1769
+ readonly EQUATION_DEFINES_PARAMETER: "equation_defines_parameter";
1770
+ readonly UNBOUND_INDEX_SYMBOL: "unbound_index_symbol";
1700
1771
  readonly EVENT_VAR_UNDECLARED: "event_var_undeclared";
1701
1772
  readonly FACTOR_WITH_EXPRESSION_TRANSFORM: "factor_with_expression_transform";
1702
1773
  readonly IC_IN_REACTION_SYSTEM: "ic_in_reaction_system";
@@ -1705,10 +1776,15 @@ declare const ERROR_CODES: {
1705
1776
  readonly NULL_REACTION: "null_reaction";
1706
1777
  readonly AMBIGUOUS_SUBSYSTEM_REF: "ambiguous_subsystem_ref";
1707
1778
  readonly DATA_SOURCE_UNDEFINED: "data_source_undefined";
1779
+ readonly DATA_SOURCE_URL_UNRESOLVED: "data_source_url_unresolved";
1780
+ readonly RESERVED_VARIABLE_NAME: "reserved_variable_name";
1781
+ readonly ARRAY_DEFAULT_WITHOUT_SHAPE: "array_default_without_shape";
1708
1782
  readonly UNDEFINED_PARAMETER: "undefined_parameter";
1709
1783
  readonly UNDEFINED_SPECIES: "undefined_species";
1710
1784
  readonly UNDEFINED_SYSTEM: "undefined_system";
1711
1785
  readonly UNDEFINED_VARIABLE: "undefined_variable";
1786
+ readonly UNKNOWN_OVERRIDE_KEY: "unknown_override_key";
1787
+ readonly ASSERTION_RANK_MISMATCH: "assertion_rank_mismatch";
1712
1788
  readonly UNIT_ERROR: "unit_error";
1713
1789
  readonly UNIT_INCONSISTENCY: "unit_inconsistency";
1714
1790
  readonly UNIT_PARSE_ERROR: "unit_parse_error";
@@ -1723,6 +1799,8 @@ declare const ERROR_CODES: {
1723
1799
  readonly ENUM_LOWERING_ERROR: "enum_lowering_error";
1724
1800
  readonly NONFINITE_NUMBER: "nonfinite_number";
1725
1801
  readonly LOAD_ERROR: "load_error";
1802
+ readonly SOLVER_VERSION_TOO_OLD: "solver_version_too_old";
1803
+ readonly CONST_UNITS_VERSION_TOO_OLD: "const_units_version_too_old";
1726
1804
  readonly APPLY_EXPRESSION_TEMPLATE_BINDINGS_MISMATCH: "apply_expression_template_bindings_mismatch";
1727
1805
  readonly APPLY_EXPRESSION_TEMPLATE_INVALID_DECLARATION: "apply_expression_template_invalid_declaration";
1728
1806
  readonly APPLY_EXPRESSION_TEMPLATE_RECURSIVE_BODY: "apply_expression_template_recursive_body";
@@ -1730,6 +1808,7 @@ declare const ERROR_CODES: {
1730
1808
  readonly APPLY_EXPRESSION_TEMPLATE_VERSION_TOO_OLD: "apply_expression_template_version_too_old";
1731
1809
  readonly REWRITE_RULE_NONTERMINATING: "rewrite_rule_nonterminating";
1732
1810
  readonly TEMPLATE_BODY_EXPANSION_TOO_DEEP: "template_body_expansion_too_deep";
1811
+ readonly TEMPLATE_BODY_REFERENCES_COUPLING_REWRITTEN_VARIABLE: "template_body_references_coupling_rewritten_variable";
1733
1812
  readonly TEMPLATE_CONSTRAINT_UNKNOWN_INDEX_SET: "template_constraint_unknown_index_set";
1734
1813
  readonly METAPARAMETER_NAME_CONFLICT: "metaparameter_name_conflict";
1735
1814
  readonly METAPARAMETER_TYPE_ERROR: "metaparameter_type_error";
@@ -1748,9 +1827,19 @@ declare const ERROR_CODES: {
1748
1827
  readonly TEMPLATE_IMPORT_VERSION_TOO_OLD: "template_import_version_too_old";
1749
1828
  readonly TEMPLATE_INJECT_TARGET_NOT_COMPONENT: "template_inject_target_not_component";
1750
1829
  readonly TEMPLATE_INJECT_TARGET_UNKNOWN: "template_inject_target_unknown";
1830
+ readonly TEMPLATE_LIBRARY_ILLEGAL_PAYLOAD: "template_library_illegal_payload";
1751
1831
  readonly GEOMETRY_MANIFOLD_INVALID: "geometry_manifold_invalid";
1752
1832
  readonly MAKEARRAY_REGION_INVERTED: "makearray_region_inverted";
1753
1833
  readonly SUBSYSTEM_INDEX_SET_CONFLICT: "subsystem_index_set_conflict";
1834
+ readonly SUBSYSTEM_INDEX_SET_RENAME_UNKNOWN_NAME: "subsystem_index_set_rename_unknown_name";
1835
+ readonly SUBSYSTEM_INDEX_SET_RENAME_UNSUPPORTED_MOUNT_FORM: "subsystem_index_set_rename_unsupported_mount_form";
1836
+ /**
1837
+ * A §4.7 `{ ref }` mount at a form this binding does not implement — a
1838
+ * top-level `reaction_systems.<k>` `{ ref }` (esm-spec §4.7 "Two mount forms,
1839
+ * one mechanism"). Refused at `/reaction_systems/<k>` rather than left as an
1840
+ * unresolved stub.
1841
+ */
1842
+ readonly MOUNT_FORM_UNSUPPORTED: "mount_form_unsupported";
1754
1843
  readonly SUBSYSTEM_REF_IS_COUPLING_LIBRARY: "subsystem_ref_is_coupling_library";
1755
1844
  readonly SUBSYSTEM_REF_IS_TEMPLATE_LIBRARY: "subsystem_ref_is_template_library";
1756
1845
  readonly COUPLING_EDGE_UNKNOWN_ROLE: "coupling_edge_unknown_role";
@@ -1763,11 +1852,37 @@ declare const ERROR_CODES: {
1763
1852
  readonly COUPLING_LIBRARY_NESTED_IMPORT: "coupling_library_nested_import";
1764
1853
  readonly COUPLING_ROLE_UNUSED: "coupling_role_unused";
1765
1854
  readonly ENUM_OP_MALFORMED: "enum_op_malformed";
1766
- readonly ENUM_NOT_DECLARED: "enum_not_declared";
1767
- readonly ENUM_MEMBER_NOT_FOUND: "enum_member_not_found";
1855
+ readonly UNKNOWN_ENUM: "unknown_enum";
1856
+ readonly UNKNOWN_ENUM_SYMBOL: "unknown_enum_symbol";
1857
+ readonly TABLE_LOOKUP_UNKNOWN_TABLE: "table_lookup_unknown_table";
1858
+ readonly TABLE_LOOKUP_AXIS_NAME_MISMATCH: "table_lookup_axis_name_mismatch";
1859
+ readonly TABLE_LOOKUP_OUTPUT_OUT_OF_RANGE: "table_lookup_output_out_of_range";
1860
+ readonly TABLE_INTERPOLATION_AXES_MISMATCH: "table_interpolation_axes_mismatch";
1861
+ readonly TABLE_DATA_SHAPE_MISMATCH: "table_data_shape_mismatch";
1862
+ readonly TABLE_AXIS_NAN: "table_axis_nan";
1863
+ readonly TABLE_OUT_OF_BOUNDS_UNSUPPORTED: "table_out_of_bounds_unsupported";
1768
1864
  readonly UNKNOWN_CLOSED_FUNCTION: "unknown_closed_function";
1769
1865
  readonly CLOSED_FUNCTION_ARITY: "closed_function_arity";
1770
1866
  readonly CLOSED_FUNCTION_OVERFLOW: "closed_function_overflow";
1867
+ readonly INTERP_AXIS_LENGTH_MISMATCH: "interp_axis_length_mismatch";
1868
+ readonly INTERP_AXIS_NOT_CONST: "interp_axis_not_const";
1869
+ readonly INTERP_AXIS_TOO_SHORT: "interp_axis_too_short";
1870
+ readonly INTERP_NAN_IN_AXIS: "interp_nan_in_axis";
1871
+ readonly INTERP_NON_MONOTONIC_AXIS: "interp_non_monotonic_axis";
1872
+ readonly INTERP_TABLE_NOT_CONST: "interp_table_not_const";
1873
+ readonly SEARCHSORTED_NAN_IN_TABLE: "searchsorted_nan_in_table";
1874
+ readonly SEARCHSORTED_NON_MONOTONIC: "searchsorted_non_monotonic";
1875
+ readonly CONST_NOT_SCALAR: "const_not_scalar";
1876
+ readonly ENUM_NOT_LOWERED: "enum_not_lowered";
1877
+ readonly FN_MISSING_NAME: "fn_missing_name";
1878
+ readonly INVALID_EXPRESSION: "invalid_expression";
1879
+ readonly UNBOUND_VARIABLE: "unbound_variable";
1880
+ readonly UNLOWERED_OPERATOR: "unlowered_operator";
1881
+ readonly UNSUPPORTED_OPERATOR: "unsupported_operator";
1882
+ readonly CONFLICTING_DERIVATIVE: "conflicting_derivative";
1883
+ readonly DIMENSION_PROMOTION: "dimension_promotion";
1884
+ readonly FLATTEN_ERROR: "flatten_error";
1885
+ readonly UNEVALUABLE_OPERATOR: "unevaluable_operator";
1771
1886
  };
1772
1887
  /** A diagnostic code string from {@link ERROR_CODES}. */
1773
1888
  type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
@@ -1910,7 +2025,7 @@ declare function losslessJsonStringify(value: unknown): string;
1910
2025
  declare function formatFloatToken(value: number): string;
1911
2026
 
1912
2027
  /**
1913
- * EarthSciML Serialization Format TypeScript type definitions — plus a small
2028
+ * EarthSciML Abstract Syntax Tree Format TypeScript type definitions — plus a small
1914
2029
  * set of RUNTIME re-exports.
1915
2030
  *
1916
2031
  * Provides the complete type definitions for the ESM format: the auto-generated
@@ -2019,6 +2134,35 @@ type EsmFile = ESMFormat1 & Omit<ESMFormat2, 'models'> & {
2019
2134
  * the file object must re-attach it if the result will be flattened.
2020
2135
  */
2021
2136
  componentTemplates?: Record<string, unknown>;
2137
+ /**
2138
+ * NON-SCHEMA, LOADER-POPULATED. The loader-API metaparameter bindings this
2139
+ * document was loaded with (esm-spec §9.7.6 binding site 4) — the map a
2140
+ * discovered §8.9.4 `extent` arrives as.
2141
+ *
2142
+ * `loadInput` (`parse.ts`) attaches it so `resolveSubsystemRefsSync` can
2143
+ * BACKFILL each `subsystems.<k>` mount edge's close with it (§4.7 "Two mount
2144
+ * forms, one mechanism"): in this binding ref resolution is a separate step
2145
+ * from `load`, so the bindings have no other route from the loader call to
2146
+ * the mount edge. Filtered at the edge to the names the LEAF declares, and
2147
+ * an explicit edge `binding` always wins.
2148
+ *
2149
+ * NON-ENUMERABLE for the same reason `componentTemplates` is: the
2150
+ * conformance round-trip compares loaded documents structurally, and a
2151
+ * loader sidecar is not part of the document. Absent unless the document was
2152
+ * loaded with `metaparameters`.
2153
+ */
2154
+ loaderMetaparameters?: Record<string, number>;
2155
+ /**
2156
+ * NON-SCHEMA, LOADER-POPULATED. The directory relative `coupling_import`
2157
+ * refs resolve against (esm-spec §10.10 -> §4.7: "relative to the directory
2158
+ * of the referencing file"), recorded by `loadInput` when the load had an
2159
+ * explicit `basePath` (`loadPath` always passes the file's directory) and
2160
+ * preferred by `expandCouplingImports` over `CouplingImportOptions.basePath`.
2161
+ * NON-ENUMERABLE for the same reason `componentTemplates` is: the
2162
+ * `coupling_import` entry must round-trip verbatim (§10.10.3), so the base is
2163
+ * kept beside the document, never written into its refs.
2164
+ */
2165
+ couplingImportBase?: string;
2022
2166
  };
2023
2167
  /** @deprecated Prefer {@link ExpressionNode} (the generated name). */
2024
2168
  type ExprNode = ExpressionNode;
@@ -2270,10 +2414,63 @@ interface CanonicalDims {
2270
2414
  cd?: number;
2271
2415
  rad?: number;
2272
2416
  }
2417
+ /**
2418
+ * The EXACT scale of a unit relative to SI (esm-spec §4.8.1 "Scales are EXACT"):
2419
+ * a product of prime powers and a power of π, each with a rational exponent, so
2420
+ * `mi` is 2^4 * 3^2 * 5^-3 * 11 * 127. Multiplying units adds exponents,
2421
+ * dividing subtracts them and a rational power multiplies them, so a composite
2422
+ * unit's scale never rounds. Every scale AGREEMENT (`m + km`, `m/s = mi/h`) is
2423
+ * decided with {@link ExactScale.equals}; the floating-point `scale` is kept for
2424
+ * numeric conversion only.
2425
+ */
2426
+ declare class ExactScale {
2427
+ private readonly primes;
2428
+ private readonly piExp;
2429
+ private constructor();
2430
+ private static readonly ONE;
2431
+ /** Exactly 1. */
2432
+ static one(): ExactScale;
2433
+ /** A positive safe integer. */
2434
+ static integer(value: number): ExactScale;
2435
+ /** `num/den` for positive safe integers. */
2436
+ static ratio(num: number, den: number): ExactScale;
2437
+ /** `10^k`. */
2438
+ static pow10(k: number): ExactScale;
2439
+ /** π. */
2440
+ static pi(): ExactScale;
2441
+ /**
2442
+ * The exact value of a positive decimal literal as the registry writes it
2443
+ * (`"0.3048"`, `"133.322387415"`, `"2.6867e20"`), read from its TEXT.
2444
+ */
2445
+ static decimal(literal: string): ExactScale;
2446
+ private normalized;
2447
+ private combine;
2448
+ /** The scale of a product of units. */
2449
+ multiply(other: ExactScale): ExactScale;
2450
+ /** The scale of a quotient of units. */
2451
+ divide(other: ExactScale): ExactScale;
2452
+ /** The scale of a unit raised to a (rational) power. */
2453
+ power(exponent: number): ExactScale;
2454
+ /** Whether two scales are the same number. */
2455
+ equals(other: ExactScale): boolean;
2456
+ /** Whether this is exactly 1. */
2457
+ isOne(): boolean;
2458
+ /** The nearest double, for diagnostics and numeric conversion — never for comparison. */
2459
+ toNumber(): number;
2460
+ /**
2461
+ * `p/q`, `p/q*pi` or `p/q*pi^k` in lowest terms (`/q` omitted when it is 1),
2462
+ * the spelling tests/conformance/unit_registry pins; `null` when an exponent is
2463
+ * not whole (`sqrt(km)`).
2464
+ */
2465
+ ratioString(): string | null;
2466
+ toString(): string;
2467
+ }
2273
2468
  interface ParsedUnit {
2274
2469
  dims: CanonicalDims;
2275
2470
  scale: number;
2276
2471
  offset?: number;
2472
+ /** The scale every agreement is decided on (esm-spec §4.8.1). */
2473
+ exact: ExactScale;
2277
2474
  }
2278
2475
  declare class UnitConversionError extends EsmDiagnosticError {
2279
2476
  constructor(message: string);
@@ -2365,7 +2562,7 @@ interface UnitResult {
2365
2562
  * `null` is not "dimensionless" — it is "this analysis cannot say", and it is
2366
2563
  * the value returned for an unknown variable, an unparseable unit
2367
2564
  * declaration, and any operator whose dimensional semantics this module does
2368
- * not model (`index`, `fn`, `aggregate`, `makearray`, `table_lookup`, ...).
2565
+ * not model (`index`, `fn`, `faq`, `makearray`, `table_lookup`, ...).
2369
2566
  * Keeping the two apart is what stops a structural op from being *assumed*
2370
2567
  * dimensionless and thereby manufacturing a false mismatch against a
2371
2568
  * dimensional operand. `null` propagates through every combining rule, and a
@@ -2407,7 +2604,7 @@ interface UnitWarning {
2407
2604
  * registry gap is a false rejection.)
2408
2605
  * - `analysis` — the checker cannot DETERMINE a dimension: a symbolic
2409
2606
  * exponent (`x^n`, whose dimension depends on `n`'s runtime value), an
2410
- * operator with no dimensional rule (`aggregate`, `index`, `fn`,
2607
+ * operator with no dimensional rule (`faq`, `index`, `fn`,
2411
2608
  * `table_lookup`), a malformed arity, an unknown variable. Genuinely
2412
2609
  * undeterminable — a statement about the checker, not the file. → WARNING,
2413
2610
  * and the dimension is reported UNKNOWN and the check SKIPPED, never assumed
@@ -3068,7 +3265,26 @@ declare class CadenceSeeder {
3068
3265
  private readonly memo;
3069
3266
  private readonly inProgress;
3070
3267
  private readonly independentVariable;
3268
+ /**
3269
+ * Memo for {@link isRecurrence}. A recurrence body reads itself once per lag —
3270
+ * 38 times in `tests/fixtures/recurrence/07_recurrence_thirty_eight_lags.esm`
3271
+ * — and each read reaches `leaf` as a separate self-edge, so without this the
3272
+ * whole well-foundedness analysis would re-run per lag.
3273
+ */
3274
+ private readonly recurrenceMemo;
3071
3275
  constructor(model: Model, esmFile?: EsmFile | undefined);
3276
+ /**
3277
+ * Whether `name`'s defining equation is a causal-recurrence CANDIDATE --
3278
+ * array-shaped, with an `index` self-read in its own RHS -- and so whether its
3279
+ * self-edge is an ordering the §4.3.1.1 rules govern rather than a cycle.
3280
+ *
3281
+ * Candidacy, not well-foundedness: see `isRecurrenceCandidate` for why the
3282
+ * stricter predicate would mask the very codes §5.19.5 requires. Delegated
3283
+ * wholesale to `recurrence.js` so the seeder and the validator cannot disagree
3284
+ * about which equations the construct covers. Memoized because a recurrence
3285
+ * body reads itself once per lag.
3286
+ */
3287
+ private isRecurrence;
3072
3288
  /** The set of observed unknowns, for callers that want to enumerate them. */
3073
3289
  observedNames(): string[];
3074
3290
  /**
@@ -3795,8 +4011,8 @@ declare function toMathML(expr: Expr | Equation | Model | ReactionSystem | React
3795
4011
  * functions, derivatives (`D(x)/Dt` and `D(x, t)`), open/user function calls;
3796
4012
  * - array & call-shaped tier: array literals `[…]` (`const`), indexing
3797
4013
  * `a[i, j]` (`index`), dotted closed-function calls `datetime.year(t)` (`fn`),
3798
- * the `true` literal, and `integral` / `reshape` / `transpose` / `concat`;
3799
- * - reduction & array-query tier: `aggregate` reductions
4014
+ * the `true` and `false` literals, and `integral` / `reshape` / `transpose` / `concat`;
4015
+ * - reduction & array-query tier: `faq` reductions
3800
4016
  * `sum[i] (expr) where {i in set, j in lo:hi} join(a=b) if pred distinct
3801
4017
  * key=k [semiring=…]` (all clause shapes), the `argmin`/`argmax` arg-witnesses
3802
4018
  * `argmin[g] (expr) where {…}`, template application
@@ -4266,20 +4482,70 @@ declare function contains(expr: Expr, varName: string): boolean;
4266
4482
  */
4267
4483
  declare function simplify(expr: Expr): Expr;
4268
4484
 
4485
+ /**
4486
+ * `table_lookup` → `interp.linear` / `interp.bilinear` / `index` lowering —
4487
+ * esm-spec §9.5.3.
4488
+ *
4489
+ * A `table_lookup` node is SUGAR. It names a `function_tables` entry plus one
4490
+ * input expression per declared axis, and §9.5.3 gives the exact §9.2
4491
+ * closed-function tree it stands for. Nothing in this binding evaluated
4492
+ * `table_lookup` itself: the op is absent from the evaluable-core op registry,
4493
+ * so `evaluateExpression` refused it as an `unlowered_operator` — and the only
4494
+ * lowering that existed lived INSIDE a test harness
4495
+ * (`function-tables-lowering.test.ts`). A document whose observed is defined by
4496
+ * a `table_lookup` therefore validated, round-tripped, and then failed every
4497
+ * evaluation that depended on it, while the same lookup written out by hand in
4498
+ * the lowered form worked (issue #188).
4499
+ *
4500
+ * **This runs at EVALUATION, not at load, and that is the point.** §9.5.4 makes
4501
+ * `function_tables` / `table_lookup` first-class AUTHORED constructs that must
4502
+ * survive a round trip, and `toJson` serializes the typed `EsmFile` that
4503
+ * `loadString` produced — so lowering in `parse.ts` (where `lowerEnums` sits)
4504
+ * would emit the lowered `fn` form and break §9.5.4. §9.5.3 admits either an
4505
+ * in-memory transformation or a direct evaluator dispatch; `codegen.ts`
4506
+ * dispatches on the node and lowers it here, one node at a time, leaving the
4507
+ * loaded image authored-as-written.
4508
+ *
4509
+ * Bit-equivalence with the hand-written inline-`const` lookup (§9.5's central
4510
+ * promise) comes for free: the lowered tree drives the very same
4511
+ * `closed-functions.ts` `interp.linear` / `interp.bilinear` implementations an
4512
+ * author would have invoked by hand.
4513
+ *
4514
+ * **`out_of_bounds: "error"` is REFUSED, not silently clamped.** `"clamp"` is
4515
+ * required of every binding and `"error"` is "conformant when implemented"
4516
+ * (§9.5.1); this binding does not implement it, so §9.5.3a makes the lookup a
4517
+ * `table_out_of_bounds_unsupported` error here rather than an `interp.*` tree
4518
+ * that answers in a mode the author did not ask for. That would be the same
4519
+ * defect as the one this module exists to fix: a wrong number with nothing in
4520
+ * the result to say so. The document still LOADS and still round-trips — it
4521
+ * simply does not evaluate.
4522
+ */
4523
+
4524
+ /** A document's `function_tables` block (esm-spec §9.5.1), keyed by table id. */
4525
+ type FunctionTables = {
4526
+ [k: string]: FunctionTable;
4527
+ };
4528
+
4269
4529
  /**
4270
4530
  * Tree-walking scalar evaluator (`compileExpression` / `evaluateExpression`)
4271
4531
  * — the EarthSciAST TypeScript in-process runner. Despite the historical
4272
- * "codegen" filename this performs NO code generation or lowering: a
4273
- * canonical-form `Expr` is walked directly. `compileExpression` returns a
4274
- * closure over a free-variable bindings map that returns the scalar numeric
4275
- * result; `evaluateExpression` walks and applies in one step.
4532
+ * "codegen" filename this performs NO code generation: a canonical-form `Expr`
4533
+ * is walked directly. `compileExpression` returns a closure over a
4534
+ * free-variable bindings map that returns the scalar numeric result;
4535
+ * `evaluateExpression` walks and applies in one step.
4276
4536
  *
4277
4537
  * Structural / array ops and the closed-function registry are dispatched to
4278
- * their consumers; ANY op the evaluable-core op-registry does not know — the
4279
- * open-tier rewrite-target sugar `grad`/`div`/`laplacian`/`integral`, a user op,
4280
- * or a spatial / right-hand-side `D` — is rejected here as an unlowered
4281
- * rewrite-target: it must be lowered to a stencil by a rewrite rule before
4282
- * evaluation.
4538
+ * their consumers. `table_lookup` is the one op lowered rather than dispatched
4539
+ * — to its §9.5.3 `interp.*` form, one node at a time, on the way through; see
4540
+ * `lower-table-lookups.ts` for why that happens HERE and not at load.
4541
+ *
4542
+ * Both entry points walk the whole expression BEFORE evaluating any of it
4543
+ * (esm-spec §9.6.6) and refuse an operator this evaluator cannot evaluate: an op
4544
+ * outside the §4.2 evaluable core — the open-tier sugar
4545
+ * `grad`/`div`/`laplacian`/`integral`, a user op, or a spatial / right-hand-side
4546
+ * `D` — with `unlowered_operator`, and a core op with no scalar rule — the
4547
+ * array/query and value-invention ops, `Pre`, an unlowered `enum` — with
4548
+ * `unevaluable_operator`. An op in an untaken `ifelse` branch is refused too.
4283
4549
  */
4284
4550
 
4285
4551
  /**
@@ -4288,6 +4554,19 @@ declare function simplify(expr: Expr): Expr;
4288
4554
  * returns the scalar result.
4289
4555
  */
4290
4556
  type CompiledExpression = (bindings: Map<string, number>) => number;
4557
+ /**
4558
+ * Document context an expression may need beyond its free-variable bindings.
4559
+ *
4560
+ * Only `table_lookup` needs any: the node names a `function_tables` entry that
4561
+ * lives on the DOCUMENT, not in the expression, so an expression lifted out of
4562
+ * an `EsmFile` cannot be evaluated without being handed the block it refers to
4563
+ * (`{ functionTables: file.function_tables }`). Every other op is
4564
+ * self-contained, which is why this is optional.
4565
+ */
4566
+ interface EvaluateOptions {
4567
+ /** The document's `function_tables` block (esm-spec §9.5.1). */
4568
+ functionTables?: FunctionTables | undefined;
4569
+ }
4291
4570
  /**
4292
4571
  * Error carrying the stable, cross-binding `unlowered_operator` diagnostic
4293
4572
  * (esm-spec §4.2 / §9.6.3 constraint 6 / §9.6.8). Raised when a rewrite-target
@@ -4298,9 +4577,23 @@ type CompiledExpression = (bindings: Map<string, number>) => number;
4298
4577
  * gate in tree_walk.jl.
4299
4578
  */
4300
4579
  declare class UnloweredOperatorError extends EsmDiagnosticError {
4301
- readonly code: 'unlowered_operator';
4580
+ readonly code: typeof ERROR_CODES.UNLOWERED_OPERATOR;
4302
4581
  constructor(message: string);
4303
4582
  }
4583
+ /**
4584
+ * Error carrying the stable, cross-binding `unevaluable_operator` diagnostic
4585
+ * (esm-spec §9.6.6): an op that IS in the §4.2 evaluable-core set but that this
4586
+ * scalar evaluator has no rule for. The complement of
4587
+ * {@link UnloweredOperatorError}, which is for an op OUTSIDE the core, so it
4588
+ * carries no rewrite-rule advice: the op needs an earlier pipeline stage (value
4589
+ * invention, or a load-time lowering pass), not a rewrite rule.
4590
+ */
4591
+ declare class UnevaluableOperatorError extends EsmDiagnosticError {
4592
+ readonly code: typeof ERROR_CODES.UNEVALUABLE_OPERATOR;
4593
+ /** The offending operator name. */
4594
+ readonly op: string;
4595
+ constructor(op: string, remedy?: string);
4596
+ }
4304
4597
  /**
4305
4598
  * Error raised by the tree-walking evaluator for a node it cannot reduce to a
4306
4599
  * scalar: an unbound variable, an unsupported operator, an unlowered `enum`, a
@@ -4326,8 +4619,11 @@ declare class EvaluatorError extends EsmDiagnosticError {
4326
4619
  * load time) and array-valued `const` nodes (those are consumed by
4327
4620
  * container ops such as `interp.searchsorted` and `index`, not by
4328
4621
  * scalar evaluation).
4622
+ *
4623
+ * Pass `{ functionTables: file.function_tables }` to evaluate an expression
4624
+ * containing `table_lookup` nodes — see {@link EvaluateOptions}.
4329
4625
  */
4330
- declare function compileExpression(expr: Expr): CompiledExpression;
4626
+ declare function compileExpression(expr: Expr, options?: EvaluateOptions): CompiledExpression;
4331
4627
  /**
4332
4628
  * Compile and apply in one step. Equivalent to
4333
4629
  * `compileExpression(expr)(bindings)` but avoids allocating a closure
@@ -4335,7 +4631,7 @@ declare function compileExpression(expr: Expr): CompiledExpression;
4335
4631
  * fixed-point observed-variable resolution, unit-conversion
4336
4632
  * folding).
4337
4633
  */
4338
- declare function evaluateExpression(expr: Expr, bindings: Map<string, number>): number;
4634
+ declare function evaluateExpression(expr: Expr, bindings: Map<string, number>, options?: EvaluateOptions): number;
4339
4635
 
4340
4636
  /**
4341
4637
  * Migration utilities for ESM format version upgrades.
@@ -4533,6 +4829,51 @@ declare class ConflictingDerivativeError extends FlattenError {
4533
4829
  declare class CoupleMultiplicativeNoTendencyError extends FlattenError {
4534
4830
  constructor(message: string);
4535
4831
  }
4832
+ /**
4833
+ * An `operator_compose` entry merged NOTHING (esm-libraries-spec §4.7.1 step 5).
4834
+ *
4835
+ * Such an entry is indistinguishable from one that is not there: the operator
4836
+ * integrates a private, decoupled system from its own defaults, the other system
4837
+ * receives no contribution at all, and the only evidence is a state count one too
4838
+ * high. That is the one outcome a coupling mis-specification must not have, so it
4839
+ * is refused rather than reported.
4840
+ *
4841
+ * An operator that genuinely contributes only states of its own — a transport
4842
+ * operator whose single equation defines its own wind field, say — says so with
4843
+ * `require_match: false`, and is then permitted.
4844
+ */
4845
+ declare class OperatorComposeNoMergeError extends FlattenError {
4846
+ constructor(message: string);
4847
+ }
4848
+ /**
4849
+ * An `operator_compose` entry declared `require_match: true` and one of
4850
+ * `systems[1]`'s equations found no equation of `systems[0]` to land on
4851
+ * (esm-libraries-spec §4.7.1 step 5).
4852
+ *
4853
+ * Step 5 otherwise preserves an unmatched equation unchanged, and a PARTIAL
4854
+ * shortfall is only a warning by default; `require_match` is the author's opt-in
4855
+ * to make it fatal. A PARTIAL match throws here — there is no "some is enough"
4856
+ * reading an author could rely on.
4857
+ */
4858
+ declare class OperatorComposeRequireMatchError extends FlattenError {
4859
+ constructor(message: string);
4860
+ }
4861
+ /**
4862
+ * The bare-name fallback would unify two STATE variables and the document has
4863
+ * not said which spelling survives (esm-libraries-spec §4.7.1 step 3).
4864
+ *
4865
+ * The fallback binds `A.x` to `B.x` on the strength of a shared local name alone.
4866
+ * When both are states, each carries its own INITIAL CONDITION, and the merge has
4867
+ * to delete one of them — so the choice decides which IC the flattened system
4868
+ * integrates from. Nothing in the document expresses that choice, and picking one
4869
+ * silently is how flipping the entry's `systems` order came to change the answer.
4870
+ *
4871
+ * A match where only ONE side is a state is NOT ambiguous: the other carries no
4872
+ * initial condition, so the state is the owner and the merge renames onto it.
4873
+ */
4874
+ declare class OperatorComposeAmbiguousBareNameError extends FlattenError {
4875
+ constructor(message: string);
4876
+ }
4536
4877
  /**
4537
4878
  * An `identity`-transform `variable_map` bridges two variables whose declared,
4538
4879
  * non-empty units differ (esm-libraries-spec §4.7.6). `conversion_factor` and
@@ -4554,10 +4895,13 @@ declare class DimensionPromotionError extends FlattenError {
4554
4895
  * The DERIVED role of a flattened variable (esm-spec §6.3.1) — never a declared
4555
4896
  * type, which from 1.0.0 is only `unknown` or `parameter`.
4556
4897
  *
4557
- * - `state` — solved for: an ODE state, an algebraic unknown, or an arrayed
4558
- * observed that materializes into a buffer.
4559
- * - `observed` — an unknown a bare-variable-LHS equation defines, eliminable by
4560
- * inlining.
4898
+ * - `state` — solved for and defined by no equation of its own: an ODE state or
4899
+ * an algebraic unknown.
4900
+ * - `observed` — an unknown an equation DEFINES, at either LHS spelling. A
4901
+ * scalar one is eliminable by inlining and appears in `observedVariables`
4902
+ * alone; an arrayed one materializes into a buffer and appears in
4903
+ * `stateVariables` too, keeping this role in both (esm-libraries-spec §4.7.5
4904
+ * step 4: `stateVariables` is the solved-for vector, not a classification).
4561
4905
  * - `parameter` — a parameter of any cadence.
4562
4906
  * - `species` — a reaction-system state (a `state` that came from a species).
4563
4907
  */
@@ -4581,7 +4925,11 @@ interface FlattenedVariable {
4581
4925
  sourceSystem?: string;
4582
4926
  /** Ordered index-set names for an arrayed variable; absent means scalar. */
4583
4927
  shape?: string[];
4584
- /** The declared cadence machinery, carried verbatim (parameters only). */
4928
+ /**
4929
+ * The declared cadence machinery (parameters only), with its expression
4930
+ * slots — a `condition` / `crossing` `when` and the value `expression` —
4931
+ * namespaced like any other reference (§4.7.5 step 2).
4932
+ */
4585
4933
  update?: ParameterUpdateSpec;
4586
4934
  /** The declared sampling law, carried verbatim (parameters only). */
4587
4935
  distribution?: Distribution;
@@ -4652,6 +5000,29 @@ interface FlattenMetadata {
4652
5000
  operatorApplies: string[];
4653
5001
  /** `callback` entries, recorded as opaque runtime references. */
4654
5002
  callbacks: string[];
5003
+ /**
5004
+ * Every state spelling an `operator_compose` renaming match DELETED, mapped
5005
+ * onto the survivor it was folded into (issue #230).
5006
+ *
5007
+ * The flattener's own retarget reaches equation ASTs; this is the map a
5008
+ * CONSUMER that addresses a state by name needs — a `parameter_overrides` /
5009
+ * `initial_conditions` key, an output selection — to resolve a spelling the
5010
+ * merge moved out from under it. Empty for a document with no renaming merge,
5011
+ * which is the overwhelming majority.
5012
+ */
5013
+ mergedVariableRenames: Record<string, string>;
5014
+ /**
5015
+ * Every name a COUPLING rule rewrote OUT of the flattened equations: a
5016
+ * `variable_map`'s substituted target, plus every merged-away spelling of
5017
+ * {@link FlattenMetadata.mergedVariableRenames}.
5018
+ *
5019
+ * Only the registry guard reads it. A surviving `expression_templates` body
5020
+ * is authored source that no equation walk reaches, so a body naming one of
5021
+ * these is REFUSED rather than resolved (see `checkRegistryCouplingRewrites`,
5022
+ * CONFORMANCE_SPEC §5.35). Unlike `mergedVariableRenames` this is not a
5023
+ * consumer-facing map: it says a name is GONE, not where it went.
5024
+ */
5025
+ couplingRewrittenNames: Set<string>;
4655
5026
  }
4656
5027
  /** A deferred `ic` equation (esm-spec §11.4.1): an initial condition, not dynamics. */
4657
5028
  interface FieldInitialCondition {
@@ -4814,7 +5185,10 @@ declare class CircularReferenceError extends EsmDiagnosticError {
4814
5185
  declare class RefLoadError extends EsmDiagnosticError {
4815
5186
  /** The reference path or URL that failed to load */
4816
5187
  readonly ref: string;
4817
- /** Canonical code: `unresolved_subsystem_ref` or `ambiguous_subsystem_ref`. */
5188
+ /**
5189
+ * Canonical code: `unresolved_subsystem_ref`, `ambiguous_subsystem_ref`, or
5190
+ * `mount_form_unsupported` for a mount form this binding does not implement.
5191
+ */
4818
5192
  readonly code: string;
4819
5193
  /**
4820
5194
  * JSON Pointer of the SUBSYSTEM ENTRY that carries the bad ref (e.g.
@@ -4836,7 +5210,7 @@ declare class RefLoadError extends EsmDiagnosticError {
4836
5210
  * cycle detection and component inlining exist in exactly one place, so the sync
4837
5211
  * and async paths cannot drift apart.
4838
5212
  */
4839
- declare function resolveSubsystemRefs(file: EsmFile, basePath: string): Promise<void>;
5213
+ declare function resolveSubsystemRefs(file: EsmFile, basePath: string, loaderMetaparameters?: Readonly<Record<string, number>>): Promise<void>;
4840
5214
  /**
4841
5215
  * esm-spec §9.7.10 form C: build a throwaway `EsmFile` in which component
4842
5216
  * `mname` has the run's `imports` (raw §9.7.2 entries) appended to its own
@@ -4849,7 +5223,7 @@ declare function resolveSubsystemRefs(file: EsmFile, basePath: string): Promise<
4849
5223
  * The raw base is re-read from `sourcePath` when given (relative import `ref`s
4850
5224
  * resolve against its directory), else re-serialized from `file`; `baseDir`
4851
5225
  * anchors the injected `ref`s. Mirrors the Julia reference
4852
- * `_ephemeral_injected_file` (`EarthSciAST.jl/src/pde_inline_tests.jl`).
5226
+ * `_ephemeral_injected_file` (`EarthSciAST.jl/src/inline_tests.jl`).
4853
5227
  *
4854
5228
  * This binding does not numerically simulate PDEs; the ephemeral build is the
4855
5229
  * structural-lowering half of form C (the leaf's rewrite-target is lowered in
@@ -5178,10 +5552,10 @@ declare function dispatchClosedFunction(name: string, args: unknown[]): number;
5178
5552
  * enum):
5179
5553
  * - `enum_op_malformed` — an `enum` op whose args are not
5180
5554
  * `[enum_name, member_name]` (two strings).
5181
- * - `enum_not_declared` — reference to an enum name not present in the file's
5182
- * top-level `enums` block.
5183
- * - `enum_member_not_found` — reference to an unknown member of a declared
5184
- * enum.
5555
+ * - `unknown_enum` — reference to an enum name not present in the file's
5556
+ * top-level `enums` block (esm-spec §4.5).
5557
+ * - `unknown_enum_symbol` — reference to an unknown member of a declared
5558
+ * enum (esm-spec §4.5).
5185
5559
  */
5186
5560
 
5187
5561
  declare class EnumLoweringError extends EsmDiagnosticError {
@@ -5411,6 +5785,22 @@ interface TemplateResolveOptions {
5411
5785
  * reference). Supplied by the `load*` entry points; optional for direct/raw use.
5412
5786
  */
5413
5787
  validateSchema?: ((raw: unknown) => TemplateSchemaError[]) | undefined;
5788
+ /**
5789
+ * Metaparameter names declared by the documents this one MOUNTS (esm-spec
5790
+ * §4.7, either mount form, transitively) — see
5791
+ * {@link collectMountDeclaredMetaparameters}. Widens the §9.7.6 site-4
5792
+ * check: a loader-API binding naming one of these is meaningful even though
5793
+ * THIS document does not declare it, because the mount edge forwards it into
5794
+ * the leaf's own close.
5795
+ */
5796
+ mountDeclared?: ReadonlySet<string> | undefined;
5797
+ /**
5798
+ * This call IS a §4.7 mount edge, so index-set sizes this scope cannot close
5799
+ * stay symbolic for the MOUNTING document's registry to close (§9.7.6 site
5800
+ * 5) instead of being `metaparameter_unbound` here. A root document leaves it
5801
+ * unset.
5802
+ */
5803
+ mountedLeaf?: boolean | undefined;
5414
5804
  }
5415
5805
  /**
5416
5806
  * `expression_template_imports`, top-level `expression_templates`
@@ -5465,6 +5855,56 @@ declare function emitDocument(rawSource: unknown, basePath: string, options?: Te
5465
5855
  */
5466
5856
  declare function applyScopeInjections(raw: unknown, injected?: readonly unknown[]): JsonObject | null;
5467
5857
 
5858
+ /**
5859
+ * Document-scoped solver hints (esm-spec §2.2).
5860
+ *
5861
+ * The `solver` block records numerics the document knows about *itself* —
5862
+ * stiffness, integration tolerances, and a splitting hint — which each binding
5863
+ * maps to its own integrator.
5864
+ *
5865
+ * Every field is ADVISORY: a binding may ignore any or all of them and still
5866
+ * conform. Advisory governs the MECHANISM, never the OUTCOME — the
5867
+ * CONFORMANCE_SPEC §5.9 requirement to integrate successfully and agree within
5868
+ * the error band is untouched by this block and is not excused by it.
5869
+ *
5870
+ * This module carries the parts that are NOT advisory: the spec-version gate
5871
+ * (§2.2.4) and the §2.2.2 tolerance resolution order. TypeScript ships no
5872
+ * integrator, so `resolveTolerances` exists for parity and for callers driving
5873
+ * another runtime.
5874
+ */
5875
+
5876
+ /** Binding defaults (API_SPEC §5.8) — the bottom of the §2.2.2 chain. */
5877
+ declare const DEFAULT_RELTOL = 0.0001;
5878
+ declare const DEFAULT_ABSTOL = 0.000001;
5879
+ /**
5880
+ * Reject a top-level `solver` block in a file declaring esm < 1.1.0.
5881
+ *
5882
+ * The block arrives at `esm: 1.1.0`; a document declaring an earlier version
5883
+ * that carries one is rejected with `solver_version_too_old` (esm-spec
5884
+ * §2.2.4). Mirrors `rejectTemplateImportsPreV08`.
5885
+ */
5886
+ declare function rejectSolverPreV11(view: unknown): void;
5887
+ /**
5888
+ * Resolve integration tolerances most-specific first (esm-spec §2.2.2):
5889
+ *
5890
+ * 1. An explicit argument at the call site — wins outright.
5891
+ * 2. Otherwise the document's `solver.abstol` / `solver.reltol`.
5892
+ * 3. Otherwise the binding default (`reltol` 1e-4, `abstol` 1e-6).
5893
+ *
5894
+ * The two resolve INDEPENDENTLY, so a document declaring only `reltol` leaves
5895
+ * `abstol` on the default — the same per-field fall-through §6.6.4 uses.
5896
+ *
5897
+ * These are INTEGRATION tolerances, a different quantity from the `tolerance`
5898
+ * object an assertion is COMPARED at (§6.6.4).
5899
+ */
5900
+ declare function resolveTolerances(solver: Solver | undefined | null, opts?: {
5901
+ abstol?: number;
5902
+ reltol?: number;
5903
+ }): {
5904
+ abstol: number;
5905
+ reltol: number;
5906
+ };
5907
+
5468
5908
  /**
5469
5909
  * The package's OWN version — distinct from {@link SCHEMA_VERSION}, which is
5470
5910
  * the `.esm` FORMAT version this build implements. The two are unrelated
@@ -5478,7 +5918,7 @@ declare function applyScopeInjections(raw: unknown, injected?: readonly unknown[
5478
5918
  * hosts — so the value is mirrored, and `version.test.ts` fails the build if
5479
5919
  * the mirror drifts. Same arrangement Rust uses for its `SCHEMA_VERSION`.
5480
5920
  */
5481
- declare const LIBRARY_VERSION = "0.1.1";
5921
+ declare const LIBRARY_VERSION = "0.3.0";
5482
5922
 
5483
- export { CLOSED_FUNCTION_NAMES, CadenceCycleError, CadenceSeeder, CanonicalNonfiniteError, CanonicalizeError, CircularReferenceError, ClosedFunctionError, ConflictingDerivativeError, CoupleMultiplicativeNoTendencyError, DEFAULT_MIN_COMPLEXITY, DimensionPromotionError, DomainUnitMismatchError, ERROR_CODES, E_CANONICAL_DIVBY_ZERO, E_CANONICAL_NONFINITE, EdgeKind, EntityNotFoundError, EnumLoweringError, EsmDiagnosticError, EsmMachineryError, EvaluatorError, expandDocument as Expand, ExpressionParseError, EsmMachineryError as ExpressionTemplateError, FlattenError, InvalidDerivativeOrderError, LIBRARY_VERSION, LosslessJsonParseError, MAX_TEMPLATE_EXPANSION_DEPTH, MigrationError, NonDifferentiableExpressionError, ParseError, RefLoadError, ReferenceGraph, ReferenceResolutionError, SCHEMA_VERSION, SchemaValidationError, UnitConversionError, UnloweredOperatorError, VariableInUseError, VertexKind, addContinuousEvent, addCoupling, addDiscreteEvent, addEquation, addReaction, addSpecies, addVariable, algebraicUnknowns, analyzeComplexity, analyzeExpression, applyScopeInjections, authoredTemplateNames, brownianParameters, buildDependencyGraph, buildEmittedDocument, buildReferenceGraph, canMigrate, canonicalJson, canonicalize, checkDimensions, classifyComplexity, classifyDocument, classifyModel, closedFunctionNames, compareComplexity, compileExpression, componentExists, componentGraph, compose, constantParameters, contains, convertUnits, declaredSystemKind, deriveODEs, deriveOdes, detectStabilityIssues, differentiate, discreteParameters, dispatchClosedFunction, effectiveSystemKind, emitDocument, emitEsmString, ephemeralInjectedFile, estimateParallelPotential, estimateSavings, evaluateExpression, expandCouplingImports, expandDocument, expressionCadence, expressionGraph, extract, findCommonSubexpressions, findCommonSubexpressionsAcrossExpressions, findCommonSubexpressionsInEsmFile, findCommonSubexpressionsInModel, findCriticalPoints, findDeadVariables, findDependencyChains, findExpensiveSubexpressions, flatten, flattenTemplateRegistries, floatLit, formatCanonicalFloat, formatChemicalName, formatFloatToken, freeParameters, freeVariables, generateFactoredVariableNames, getComponentType, getSupportedMigrationTargets, gradient, groupSubexpressionsByType, higherOrderDerivative, intLit, interpBilinear, interpLinear, isCouplingLibraryDoc, isDifferentiable, isFloatLit, isIntLit, isNumericLiteral, isOdeState, isTemplateLibraryDoc, joinAll, joinCadence, leafCadence, loadDocument, loadPath, loadString, losslessJsonParse, losslessJsonStringify, lowerEnums, lowerExpressionTemplates, mapVariable, merge, migrate, numericValue, observedDefinitions, observedUnknowns, odeStates, parameterClass, parameters, parseEquation, parseExpression, parseReaction, parseUnit, parseUnitForConversion, partialDerivatives, productMatrix, rejectExpressionTemplatesPreV04, rejectTemplateImportsPreV08, removeCoupling, removeEquation, removeEvent, removeReaction, removeSpecies, removeVariable, renameVariable, resolveReferences, resolveSubsystemRefs, resolveTemplateMachinery, sampledParameters, searchsortedFirst, simplify, stoichiometricMatrix, substitute, substituteInEquations, substituteInModel, substituteInReactionSystem, substrateMatrix, supportedMigrationTargets, systemKind, toAscii, toDot, toJson, toJsonCompact, toJsonGraph, toLatex, toMathML, toMermaid, toUnicode, tryParseUnit, unitsCompatible, unknowns, updateRules, validate, validateInterpAxis, validateSchema, validateSearchsortedTable, validateText, validateUnits, writePath };
5484
- export type { AffectEquation, Analysis, AnalysisOptions, AnalysisResults, Assertion, Assertion1, CadenceClass, CanonicalDims, ClosedFunctionErrorCode, CommonSubexpression, CompiledExpression, ComplexityMetrics, ComponentGraph, ComponentNode, ConditionUpdate, ConnectorEquation, ContinuousEvent, Coordinate, Coordinate1, CouplingCallback, CouplingCouple, CouplingEdge, CouplingEntry, CouplingEvent, CouplingEvent1, CouplingImport, CouplingImportOptions, CouplingOperatorCompose, CouplingVariableMap, CouplingVariableMap1, CovarianceMatrix, CrossingUpdate, DataSource, DataSourceBinding, DataSourceCodes, DataSourceDeterminism, DataSourceExtent, DataSourceLocation, DataSourceRecordFilter, DataSourceSelect, DataSourceSelectAxis, DataSourceTemporal, DataUpdate, DependencyEdge, DependencyGraph, DependencyNode, DependencyRelation, DerivativeResult, DiscreteEvent, DiscreteEventTrigger, Distribution, Domain, ESMFormat, ESMFormat1, ESMFormat2, EnumDeclaration, Equation, ErrorCode, EsmFile, Expr, ExprNode, ExprNodeOf, Expression, ExpressionLocation, ExpressionNode, ExpressionNode1, ExpressionTemplate, FieldInitialCondition, FlattenMetadata, FlattenOptions, FlattenedEquation, FlattenedSystem, FlattenedTemplateRegistries, FlattenedVariable, FlattenedVariableMap, FlattenedVariableRole, FunctionTable, FunctionTableAxis, FunctionalUpdate, Graph, IndexSet, IndexSet1, LoadOptions, LoaderField, LognormalDistribution, Metadata, MetaparameterExpression, Metaparameters, Model, ModelClassification, ModelVariable, ModelVariable1, NonWienerParameterUpdate, NormalDistribution, NumericLiteral, Parameter, Parameter1, ParameterClass, ParameterSweep, ParameterUpdate, ParameterUpdateSpec, ParsedUnit, Plot, Plot1, PlotAxis, PlotSeries, PlotValue, Reaction, ReactionSystem, Reference, ReferenceEdge, ReferenceVertex, RemeshUpdate, ScheduleUpdate, SchemaError, Species, StabilityIssue, StoichiometryEntry, SubsystemRef, SweepDimension, SweepDimension1, SweepRange, SystemKind, TemplateImport, TemplateResolveOptions, TemplateSchemaError, Test, TimeSpan, ToJsonOptions, Tolerance, Tolerance1, Tolerance2, Tolerance3, TranslateTarget, UniformDistribution, UnitResult, UnitWarning, UnknownClass, UpdateValueForm, ValidationError, ValidationResult, VariableKind, VariableNode, WienerUpdate };
5923
+ export { CLOSED_FUNCTION_NAMES, CadenceCycleError, CadenceSeeder, CanonicalNonfiniteError, CanonicalizeError, CircularReferenceError, ClosedFunctionError, ConflictingDerivativeError, CoupleMultiplicativeNoTendencyError, DEFAULT_ABSTOL, DEFAULT_MIN_COMPLEXITY, DEFAULT_RELTOL, DimensionPromotionError, DomainUnitMismatchError, ERROR_CODES, E_CANONICAL_DIVBY_ZERO, E_CANONICAL_NONFINITE, EdgeKind, EntityNotFoundError, EnumLoweringError, EsmDiagnosticError, EsmMachineryError, EvaluatorError, expandDocument as Expand, ExpressionParseError, EsmMachineryError as ExpressionTemplateError, FlattenError, InvalidDerivativeOrderError, LIBRARY_VERSION, LosslessJsonParseError, MAX_TEMPLATE_EXPANSION_DEPTH, MigrationError, NonDifferentiableExpressionError, OperatorComposeAmbiguousBareNameError, OperatorComposeNoMergeError, OperatorComposeRequireMatchError, ParseError, RefLoadError, ReferenceGraph, ReferenceResolutionError, SCHEMA_VERSION, SchemaValidationError, UnevaluableOperatorError, UnitConversionError, UnloweredOperatorError, VariableInUseError, VertexKind, addContinuousEvent, addCoupling, addDiscreteEvent, addEquation, addReaction, addSpecies, addVariable, algebraicUnknowns, analyzeComplexity, analyzeExpression, applyScopeInjections, authoredTemplateNames, brownianParameters, buildDependencyGraph, buildEmittedDocument, buildReferenceGraph, canMigrate, canonicalJson, canonicalize, checkDimensions, classifyComplexity, classifyDocument, classifyModel, closedFunctionNames, compareComplexity, compileExpression, componentExists, componentGraph, compose, constantParameters, contains, convertUnits, declaredSystemKind, deriveODEs, deriveOdes, detectStabilityIssues, differentiate, discreteParameters, dispatchClosedFunction, effectiveSystemKind, emitDocument, emitEsmString, ephemeralInjectedFile, estimateParallelPotential, estimateSavings, evaluateExpression, expandCouplingImports, expandDocument, expressionCadence, expressionGraph, extract, findCommonSubexpressions, findCommonSubexpressionsAcrossExpressions, findCommonSubexpressionsInEsmFile, findCommonSubexpressionsInModel, findCriticalPoints, findDeadVariables, findDependencyChains, findExpensiveSubexpressions, flatten, flattenTemplateRegistries, floatLit, formatCanonicalFloat, formatChemicalName, formatFloatToken, freeParameters, freeVariables, generateFactoredVariableNames, getComponentType, getSupportedMigrationTargets, gradient, groupSubexpressionsByType, higherOrderDerivative, intLit, interpBilinear, interpLinear, isCouplingLibraryDoc, isDifferentiable, isFloatLit, isIntLit, isNumericLiteral, isOdeState, isTemplateLibraryDoc, joinAll, joinCadence, leafCadence, loadDocument, loadPath, loadString, losslessJsonParse, losslessJsonStringify, lowerEnums, lowerExpressionTemplates, mapVariable, merge, migrate, numericValue, observedDefinitions, observedUnknowns, odeStates, parameterClass, parameters, parseEquation, parseExpression, parseReaction, parseUnit, parseUnitForConversion, partialDerivatives, productMatrix, rejectExpressionTemplatesPreV04, rejectSolverPreV11, rejectTemplateImportsPreV08, removeCoupling, removeEquation, removeEvent, removeReaction, removeSpecies, removeVariable, renameVariable, resolveReferences, resolveSubsystemRefs, resolveTemplateMachinery, resolveTolerances, sampledParameters, searchsortedFirst, simplify, stoichiometricMatrix, substitute, substituteInEquations, substituteInModel, substituteInReactionSystem, substrateMatrix, supportedMigrationTargets, systemKind, toAscii, toDot, toJson, toJsonCompact, toJsonGraph, toLatex, toMathML, toMermaid, toUnicode, tryParseUnit, unitsCompatible, unknowns, updateRules, validate, validateInterpAxis, validateSchema, validateSearchsortedTable, validateText, validateUnits, writePath };
5924
+ export type { AffectEquation, Analysis, AnalysisOptions, AnalysisResults, Assertion, Assertion1, CadenceClass, CanonicalDims, ClosedFunctionErrorCode, CommonSubexpression, CompiledExpression, ComplexityMetrics, ComponentGraph, ComponentNode, ConditionUpdate, ConnectorEquation, ContinuousEvent, Coordinate, Coordinate1, CouplingCallback, CouplingCouple, CouplingEdge, CouplingEntry, CouplingEvent, CouplingEvent1, CouplingImport, CouplingImportOptions, CouplingOperatorCompose, CouplingVariableMap, CouplingVariableMap1, CovarianceMatrix, CrossingUpdate, DataSource, DataSourceBinding, DataSourceCodes, DataSourceDeterminism, DataSourceExtent, DataSourceLocation, DataSourceRecordFilter, DataSourceSelect, DataSourceSelectAxis, DataSourceTemporal, DataUpdate, DependencyEdge, DependencyGraph, DependencyNode, DependencyRelation, DerivativeResult, DiscreteEvent, DiscreteEventTrigger, Distribution, Domain, ESMFormat, ESMFormat1, ESMFormat2, EnumDeclaration, Equation, ErrorCode, EsmFile, Expr, ExprNode, ExprNodeOf, Expression, ExpressionLocation, ExpressionNode, ExpressionNode1, ExpressionTemplate, FieldInitialCondition, FlattenMetadata, FlattenOptions, FlattenedEquation, FlattenedSystem, FlattenedTemplateRegistries, FlattenedVariable, FlattenedVariableMap, FlattenedVariableRole, FunctionTable, FunctionTableAxis, FunctionalUpdate, Graph, IndexSet, IndexSet1, LoadOptions, LoaderField, LognormalDistribution, Metadata, MetaparameterExpression, Metaparameters, Model, ModelClassification, ModelVariable, ModelVariable1, NonWienerParameterUpdate, NormalDistribution, NumericArrayLiteral, NumericLiteral, Parameter, Parameter1, ParameterClass, ParameterSweep, ParameterUpdate, ParameterUpdateSpec, ParsedUnit, Plot, Plot1, PlotAxis, PlotSeries, PlotValue, Reaction, ReactionSystem, Reference, ReferenceEdge, ReferenceVertex, RemeshUpdate, ScheduleUpdate, SchemaError, Solver, Species, StabilityIssue, StoichiometryEntry, SubsystemRef, SweepDimension, SweepDimension1, SweepRange, SystemKind, TemplateImport, TemplateResolveOptions, TemplateSchemaError, Test, TimeSpan, ToJsonOptions, Tolerance, Tolerance1, Tolerance2, Tolerance3, TranslateTarget, UniformDistribution, UnitResult, UnitWarning, UnknownClass, UpdateValueForm, ValidationError, ValidationResult, VariableKind, VariableNode, WienerUpdate };