Skip to content

FE-1219: Define and enforce a Scenario HIR for server-side Petrinaut optimization#9060

Draft
kube wants to merge 1 commit into
cf/fe-1220-petrinaut-optimization-execution-isolationfrom
cf/fe-1219-petrinaut-scenario-hir
Draft

FE-1219: Define and enforce a Scenario HIR for server-side Petrinaut optimization#9060
kube wants to merge 1 commit into
cf/fe-1220-petrinaut-optimization-execution-isolationfrom
cf/fe-1219-petrinaut-scenario-hir

Conversation

@kube

@kube kube commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

🌟 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) and scenarioInit (a statement body → record of place-name → count | token records) — lowered, typechecked, and instantiated over compiler-emitted source only.
  • Scenario orchestration extracted to compile-scenario-core.ts, shared by the unchanged sandboxed compileScenario (editor) and a new Node-only compileScenarioProgram (compile once per study, evaluate per trial). Exported only via the Node-only ./compiled-model subpath, so the main entry gains no TypeScript-compiler dependency.
  • Scenario expressions/init compile at CLI bootstrap; out-of-subset code fails study startup with the offending manifest path. Math.random() is a compile error on scenario surfaces (determinism).
  • petrinaut-cli: createOptimizationProtocol compiles the scenario program once at bootstrap; per-trial optimization.evaluate runs the pre-instantiated evaluators. No sandboxed compileScenario references remain in CLI source.
  • Review fixes: rejects a const/callback parameter shadowing the scenario/parameters inputs (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:

  • modifies an npm-publishable library and I have added a changeset file(s)

📜 Does this require a change to the docs?

The changes in this PR:

  • require changes to docs which are made as part of this PR

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

🛡 What tests cover this?

New petrinaut-core tests: 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. New petrinaut-cli tests: bootstrap rejection listing offending paths, compile-once-evaluate-many, and a Function-proxy test proving no per-trial dynamic evaluation.

❓ How to test this?

  1. turbo run test:unit --filter '@hashintel/petrinaut-core' --filter '@hashintel/petrinaut-cli'
  2. Run the CLI against examples/supply-chain-profit-optimization.json and confirm it evaluates; confirm an out-of-subset scenario manifest fails at bootstrap with the offending path in the message.

…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.
@kube kube self-assigned this Jul 19, 2026
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment Jul 19, 2026 4:10am
petrinaut Ready Ready Preview, Comment Jul 19, 2026 4:10am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Jul 19, 2026 4:10am

@github-actions github-actions Bot added area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team area/apps labels Jul 19, 2026
@kube
kube marked this pull request as ready for review July 19, 2026 04:14
@kube
kube requested a review from YannisZa July 19, 2026 04:14
@cursor

cursor Bot commented Jul 19, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches the security-critical server path for user-authored optimization manifests; risk is reduced by eliminating raw new Function on manifest text, but correctness now depends on the HIR compiler/emitters and parity with the editor sandbox for edge cases like strict equality.

Overview
Server-side scenario materialization (optimization trials and CLI scenario runs) no longer evaluates manifest strings via sandboxed new Function. Parameter overrides, per-place initial-state expressions, and code-mode initial state are lowered through new HIR surfaces (scenarioExpression, scenarioInit), typechecked, emitted, and compiled once; only compiler-emitted source is instantiated. Per trial/request, the pre-built program runs shared orchestration in compile-scenario-core.ts.

The CLI calls compileScenarioProgram at optimization study bootstrap and once per scenario run request, failing startup with manifest paths when code is outside the supported subset. The in-editor path still uses compileScenario + the existing sandbox.

HIR lowering adds scenario-specific rules (e.g. no Math.random(), no redeclaring scenario/parameters, strict equality). Docs and the optimizer threat model are updated to reflect removal of the raw server execution path.

Reviewed by Cursor Bugbot for commit 718ddcd. Bugbot is set up for automated code reviews on this repo. Configure here.

@kube
kube marked this pull request as draft July 19, 2026 15:23
@kube

kube commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator Author

Converted back to draft: isolation (#9058) and Scenario HIR are deferred while the streaming fixes (#9057) and correlated logging (#9059) merge first to debug the staging network error. This branch still stacks on #9058 and will follow it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

2 participants