FE-1219: Define and enforce a Scenario HIR for server-side Petrinaut optimization#9060
Conversation
…optimization Scenario materialization on the server (Petrinaut CLI) now goes through the restricted HIR pipeline instead of sandboxed `new Function` on raw manifest strings: - New HIR surfaces `scenarioExpression` (parameter overrides, per-place initial counts) and `scenarioInit` (code-mode initial state), with lowering entry points, surface contexts, typechecking (incl. rejecting `Math.random()` for determinism) and object-convention emission. - `compileScenarioProgram` (Node-only, exported via the `compiled-model` subpath) lowers/typechecks/emits/instantiates every scenario surface once and returns a program evaluated per run/trial; only compiler-emitted source is instantiated. - The shared orchestration moved to `compile-scenario-core.ts`; the editor's sandboxed `compileScenario` is unchanged in behavior and API. - The CLI compiles the scenario program at bootstrap (manifests with code outside the supported subset fail startup listing each offending expression path) and evaluates the pre-compiled program per trial; the `run` request scenario path uses the same program. - Docs: HIR README coverage table, scenarios user guide, optimization integration guide, and the petrinaut-opt threat model updated.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
PR SummaryMedium Risk Overview The CLI calls HIR lowering adds scenario-specific rules (e.g. no Reviewed by Cursor Bugbot for commit 718ddcd. Bugbot is set up for automated code reviews on this repo. Configure here. |
🌟 What is the purpose of this PR?
Makes server-side Petrinaut scenario materialization go through the existing restricted HIR pipeline instead of executing manifest-authored strings via sandboxed
new Function, closing the last unrestricted raw-code execution path on the optimizer's server path. The in-editor sandbox path is unchanged.🔗 Related links
🚫 Blocked by
🔍 What does this change?
petrinaut-core: two new HIR surface kinds —scenarioExpression(a single expression over(parameters, scenario)→ scalar) andscenarioInit(a statement body → record of place-name → count | token records) — lowered, typechecked, and instantiated over compiler-emitted source only.compile-scenario-core.ts, shared by the unchanged sandboxedcompileScenario(editor) and a new Node-onlycompileScenarioProgram(compile once per study, evaluate per trial). Exported only via the Node-only./compiled-modelsubpath, so the main entry gains no TypeScript-compiler dependency.Math.random()is a compile error on scenario surfaces (determinism).petrinaut-cli:createOptimizationProtocolcompiles the scenario program once at bootstrap; per-trialoptimization.evaluateruns the pre-instantiated evaluators. No sandboxedcompileScenarioreferences remain in CLI source.const/callback parameter shadowing thescenario/parametersinputs (hir:redeclared-parameter), matching the sandbox; documents that server-compiled scenario equality is strict (==→===) and can differ from the editor for mixed-type comparisons.Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
🛡 What tests cover this?
New
petrinaut-coretests: a parity suite (HIR program vs sandbox for representative scenarios incl. code-mode colored tokens and the unknown-place-skip quirk), a rejection matrix (dynamic import,process,globalThis,fetch,Function, template substitution, loops,Math.random, wrapper-name shadowing), and a security test proving zero dynamic evaluation per trial. Newpetrinaut-clitests: bootstrap rejection listing offending paths, compile-once-evaluate-many, and aFunction-proxy test proving no per-trial dynamic evaluation.❓ How to test this?
turbo run test:unit --filter '@hashintel/petrinaut-core' --filter '@hashintel/petrinaut-cli'examples/supply-chain-profit-optimization.jsonand confirm it evaluates; confirm an out-of-subset scenario manifest fails at bootstrap with the offending path in the message.