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. |
Closes FE-1219 (part of FE-1217).
Stacked on #9058 (base
cf/fe-1220-...).What
Server-side Petrinaut scenario materialization now goes through the existing restricted HIR pipeline instead of executing manifest-authored strings via sandboxed
new Function. This closes the last unrestricted raw-code execution path on the optimizer's server path. The in-editor sandbox path is unchanged.petrinaut-core
scenarioExpression(a single expression over(parameters, scenario)→ scalar) andscenarioInit(a statement body → record of place-name → count | token records) — lowered through the generalized wrapped-module machinery, typechecked, and instantiated over compiler-emitted source only (instantiateHirScenarioEvaluator).compile-scenario-core.ts, shared by the unchanged sandboxedcompileScenario(editor) and the new Node-onlycompileScenarioProgram(compile once per study, evaluate per trial)../compiled-modelsubpath, so the main entry gains no TypeScript-compiler dependency.Math.random()is a compile error on scenario surfaces (determinism); scenario expressions/init compile at CLI bootstrap, so out-of-subset code fails study startup with the offending manifest path — validation happens before any trial runs.petrinaut-cli
createOptimizationProtocolcompiles the scenario program once at bootstrap; per-trialoptimization.evaluateruns the pre-instantiated evaluators. Therun-with-scenario path compiles once per request. NocompileScenario(sandbox) references remain in CLI source.Security property
Only compiler-emitted source ever reaches
new Function; user strings are JSON-quoted by the emitter, and lowering/typecheck reject dynamicimport, tagged/template literals, regex,Function, prototype/constructor access, global assignment, and loops. Verified adversarially — no raw-execution path survives.Review fixes included
const/callback parameter that shadows thescenario/parametersinputs on scenario surfaces (hir:redeclared-parameter), matching the sandbox's SyntaxError so the server no longer accepts what the editor rejects.==→===) and can differ from the editor sandbox for mixed-type comparisons (scenarios.md, hir/README.md).Tests
Includes a parity suite (HIR program vs sandbox for representative scenarios incl. code-mode colored tokens and the unknown-place-skip quirk), a rejection matrix, and a security test proving zero dynamic evaluation per trial.
Docs
User guide (
scenarios.md),OPTIMIZATION_INTEGRATION.md,hir/README.md, the threat model (raw scenario path removed), and a changeset are updated.🤖 Generated with Claude Code