Decision to make
Should Maestro-compat ${...} interpolation evaluate JavaScript expressions (GraalJS parity), or remain variable-lookup-only with loud failure?
Context
The typed engine (ADR 0015) deliberately implements ${} as literal variable lookup: ${VAR} resolves from flow config/env/runFlow env/runScript output.*; anything unresolved throws INVALID_ARGS (fail-loud — a deliberate #1217 decision replacing an earlier silent pass-through). Upstream evaluates the full ${...} payload as a GraalJS expression: arithmetic, ternaries, string concat, maestro.* namespace, JS truthiness.
The conformance oracle (#1289) now names exactly where this bites real flows:
${0 + 1} as tapOn.index (corpus upstream/032_element_index)
- full
assertTrue parity (corpus upstream/067_assertTrue_pass)
repeat.while / runFlow.when.true beyond platform comparisons
- upstream's own
065_when_true.yaml exercises ${undefined}, ${0}, ${[]}, ${{...}} as truthy conditions
Lookup-scoped subsets are being handled separately and do NOT require this decision: numeric-field ${VAR} lookup, and assertTrue: ${VAR} truthiness (phase 1).
Options
- Stay lookup-only (status quo), document as a permanent deviation in ADR 0015 + the oracle's
DOCUMENTED_DEVIATIONS. Cheapest; keeps the engine free of a JS-eval surface; flows using expressions keep failing loud with source lines.
- Bounded expression support via the existing Node
vm sandbox used by runScript (arithmetic/comparison/truthiness, no maestro.* host API). Middle ground; needs a defined subset and its own conformance vectors.
- Full GraalJS-semantics parity. Largest surface; effectively an embedded expression engine with upstream-matching semantics; only worth it if real suites demand it.
Whatever is decided, encode it: option 1 → DOCUMENTED_DEVIATIONS entry; options 2/3 → corpus flows + generated vectors so the subset boundary is mechanical, not prose.
Decision to make
Should Maestro-compat
${...}interpolation evaluate JavaScript expressions (GraalJS parity), or remain variable-lookup-only with loud failure?Context
The typed engine (ADR 0015) deliberately implements
${}as literal variable lookup:${VAR}resolves from flow config/env/runFlow env/runScriptoutput.*; anything unresolved throwsINVALID_ARGS(fail-loud — a deliberate #1217 decision replacing an earlier silent pass-through). Upstream evaluates the full${...}payload as a GraalJS expression: arithmetic, ternaries, string concat,maestro.*namespace, JS truthiness.The conformance oracle (#1289) now names exactly where this bites real flows:
${0 + 1}astapOn.index(corpusupstream/032_element_index)assertTrueparity (corpusupstream/067_assertTrue_pass)repeat.while/runFlow.when.truebeyond platform comparisons065_when_true.yamlexercises${undefined},${0},${[]},${{...}}as truthy conditionsLookup-scoped subsets are being handled separately and do NOT require this decision: numeric-field
${VAR}lookup, andassertTrue: ${VAR}truthiness (phase 1).Options
DOCUMENTED_DEVIATIONS. Cheapest; keeps the engine free of a JS-eval surface; flows using expressions keep failing loud with source lines.vmsandbox used byrunScript(arithmetic/comparison/truthiness, nomaestro.*host API). Middle ground; needs a defined subset and its own conformance vectors.Whatever is decided, encode it: option 1 →
DOCUMENTED_DEVIATIONSentry; options 2/3 → corpus flows + generated vectors so the subset boundary is mechanical, not prose.