Skip to content

FE-1485: Synthesize ad-hoc scenarios from an inline definition - #9284

Draft
kube wants to merge 2 commits into
mainfrom
cf/fe-1485-ad-hoc-scenario-synthesis-in-petrinaut-core
Draft

FE-1485: Synthesize ad-hoc scenarios from an inline definition#9284
kube wants to merge 2 commits into
mainfrom
cf/fe-1485-ad-hoc-scenario-synthesis-in-petrinaut-core

Conversation

@kube

@kube kube commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

The foundation of the ad-hoc scenario feature (FE-778): the shapes, the synthesis, and the expression type-checking infrastructure, all in petrinaut-core. Bottom of stack #9286. The design follows the ratified prototype (the "shared column values" variation handed to the designers) and the decisions from the #lib-petrinaut thread of 2026-08-06.

🔗 Related links

🔍 What does this change?

Three shapes, in pipeline order (simulation/authoring/scenario/ad-hoc-scenario.ts):

  1. AdHocScenarioState — the form's editing state: per-place token spreadsheets whose every cell is an expression, rows that are Fixed or Dynamic and mix freely, shared column values, and Variables at two scopes. Rows cycle Fixed → Dynamic → count-Optimized through cycleAdHocRowKind, retaining the count (bounds included) across the cycle.
  2. AdHocSynthesisOutput — what synthesis emits: a generated code-mode Scenario (never persisted; compileScenario is untouched) plus one AdHocOptimizedField per enabled Optimize toggle, carrying the deterministic adhoc.* parameter name, the source's attribution label (Space › item 0 › x), its target path, and its typed domain.
  3. The downstream forms: output.scenario feeds compileScenario directly (plain runs); adHocOptimizationBindings(output.optimizedFields) is the transform to a PetrinautOptimizationManifest's parameterBindings (the manifest schema itself is unchanged).

Expression vocabulary matches the scenario code editor: top-level Variables are written scenario.<name> (they stand in for scenario parameters, exactly as in the Satellites Launcher example), per-place Variables are bare names, net parameters are parameters.<name>, and i/count are per row. Net parameter overrides may read top-level Variables.

Slots (AdHocSlot = target + part, with stable path-safe adHocSlotKey strings) are the join key between synthesis errors, LSP diagnostics, and the form's rendering of both. Every synthesis error carries its slot.

Place totals: resolveAdHocPlaceTotal sums row counts — a number when everything resolves, the unresolved expressions printed otherwise — for the table's bottom line.

LSP: a new ad-hoc session kind mirrors scenario sessions end to end (protocol notifications temp/adhoc/*, virtual-file generation per non-empty value slot with scope-correct ambient declarations, service sync, worker handling, client methods). Every cell, count, Variable, net parameter override, and optimize bound gets a virtual TypeScript document type-checked as the slot's type; bounds see only the constant scope, so i in a bound is a type error.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

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

📜 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:

  • do not affect the execution graph.

🛡 What tests cover this?

24 synthesis tests — every fabricated scenario round-trips through the real compileScenario, including the scenario.<name> vocabulary, optimize suggestions applied to every generated parameter kind, shared-column supersession, the gutter cycle's retention, place totals, slot keys, and attribution labels. 5 LSP-session tests pin scope-correct diagnostics (valid expressions accept parameters.*/scenario.*/i/count/place variables; type mismatches and unknown identifiers report; i is out of scope in bounds; empty expressions produce no document; kill removes the session's files).

❓ How to test this?

turbo run test:unit --filter @hashintel/petrinaut-core

🐾 Next steps

FE-1486 (#9285) builds the form component on these shapes.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 22, 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 Aug 23, 2026 2:26pm
petrinaut Ready Ready Preview Aug 23, 2026 2:26pm
petrinaut-docs Ready Ready Preview Aug 23, 2026 2:26pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Aug 23, 2026 2:26pm

@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 labels Aug 22, 2026
An ad-hoc scenario is an initial-state + parameters definition made inline
— per-place token spreadsheets whose cells are expressions, a Static or
Dynamic count per place, and named Variables at two scopes — compiled into
a Scenario value at run time and never persisted into the net file.
Synthesis emits a code-mode scenario, the one initial-state mode whose
expressions the existing compileScenario already evaluates in its hardened
sandbox, so the compiler is untouched.

synthesizeAdHocScenario ignores Optimize toggles and serves quick
simulation and plain experiments. synthesizeAdHocOptimization turns every
Optimize selection (cell, Variable, dynamic count, net parameter) into a
generated scenario parameter with a deterministic source-derived name —
the join key from optimization results back to what the user selected —
and returns the manifest parameterBindings for them. Bounds are
expressions that must resolve to constants at study creation; an optimized
bound may not reference an optimized entity; per-place Variables evaluate
per row, so they may read i.

The tests round-trip every fabricated scenario through the real
compileScenario.
Top-level Variables are referenced as scenario.<name>, matching the scenario code editor. Synthesis emits AdHocSynthesisOutput (scenario + optimized fields with targets, labels, and domains) with adHocOptimizationBindings as the manifest transform. Adds slot keys and target labels for error and diagnostic attribution, the row-kind gutter cycle, place totals, and an ad-hoc LSP session that type-checks every value slot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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