H-6519: Add discrete token attribute types#8764
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
1 Skipped Deployment
|
PR SummaryMedium Risk Overview A shared Schemas, AI cheatsheet text, default codegen, and LSP virtual files now document typed lambdas/kernels and scenario rows ( Reviewed by Cursor Bugbot for commit 02c896f. Bugbot is set up for automated code reviews on this repo. Configure here. |
27d3d08 to
a7c19d4
Compare
a7c19d4 to
edd4758
Compare
There was a problem hiding this comment.
Pull request overview
This PR extends Petrinaut to support discrete token attribute types (integer/boolean/uuid in addition to real) across schema, simulation encoding/decoding, scenario/initial marking handling, and UI surfaces; it also introduces subnet/component-instance scaffolding (ports, instances, and active-net scoping) to support hierarchical net composition.
Changes:
- Add typed token plumbing end-to-end (schema types, codecs, frame payloads/readers, scenario compilation, and UI editors/visualizers).
- Introduce component instances + subnets and update editor state/UI to support selecting and editing an “active net” (root vs subnet).
- Update authoring surfaces (transition editors, properties panels, left sidebar, toolbar) to reflect the new structures and token typing.
Reviewed changes
Copilot reviewed 121 out of 121 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| libs/@hashintel/petrinaut/src/ui/views/shared/place-state-visualization.tsx | Use typed TokenRecord tokens from frame reader/marking. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/reactflow-types.ts | Add component-instance node types; rename arcs→edges. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/node-dimensions.ts | Add component-instance node dimensions. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/hooks/use-sdcpn-to-react-flow.ts | Build component-instance nodes and port-handle edges. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/hooks/use-apply-node-changes.ts | Include component instances in selection/position commits. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/components/viewport-settings-dialog.tsx | Add experimental “Net Components” toggle. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/components/mini-map.tsx | Render component-instance nodes in minimap. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/components/cursor-tooltip.tsx | Tooltip for add-place/transition/component modes. |
| libs/@hashintel/petrinaut/src/ui/views/SDCPN/components/component-instance-node.tsx | ReactFlow node renderer for component instances + ports. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/scenarios/view-scenario-drawer.tsx | Scenario form defaults accept typed token rows. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/scenarios/scenario-mapping.ts | Update scenario mapping comment for typed rows. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/scenarios/scenario-form.tsx | Spreadsheet initial-state data uses typed cell values. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/experiments/experiments-story-fixtures.tsx | Update fixtures for new editor actions. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/type-properties/subviews/main.tsx | Add dimension type selector (real/int/bool/uuid). |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/subviews/transition-results/subview.tsx | Kernel template generation supports component-port endpoints + typed tokens. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/subviews/transition-firing-time/subview.tsx | Guard Monaco onChange undefined values. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/subviews/main.tsx | Arc endpoint handling supports component ports in UI. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/main.tsx | Thread active net into transition properties provider. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/transition-properties/context.tsx | Provider now supplies full SDCPN + active-net info. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/properties-panel.stories.tsx | Story updates for endpoint-based arc APIs. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/subview.tsx | Pass full Place into initial-state editor. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/initial-state-editor.tsx | Spreadsheet editor supports typed values + defaults. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/main.tsx | Add “Component port” place property; use ActiveNetContext. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/panel.tsx | Add component-instance properties panel; use ActiveNetContext. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/multi-selection-panel.tsx | Add label for component instance multi-select. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/component-instance-properties/subviews/main.tsx | New component-instance properties subview. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/component-instance-properties/main.tsx | New container wiring for component-instance panel. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/component-instance-properties/context.tsx | New context for component-instance properties. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/arc-properties/main.tsx | Arc properties support endpoint keys + component ports. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/subviews/types-list.tsx | Types list uses active net. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/subviews/parameters-list.tsx | Parameters list uses active net. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/subviews/nodes-list.tsx | Nodes list uses active net; type tweaks. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/subviews/nets-list.tsx | New nets (root/subnet) selector with rename/delete. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/subviews/entities-tree.tsx | Entities tree uses ActiveNetContext for items. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/subviews/differential-equations-list.tsx | Differential equations list uses active net; null colorId default. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/panel.tsx | Gate nets subview behind extension + user setting. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/BottomPanel/subviews/simulation-timeline/legend.test.tsx | Test comment wording update. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/panels/ai-assistant-panel/tool-summaries.ts | AI tool summaries support arc endpoints + component ports. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/editor-view.tsx | Initialize SDCPN with subnets/componentInstances; reorder examples. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/components/BottomBar/use-keyboard-shortcuts.ts | “Select all” includes component instances; use active net for copy. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/components/BottomBar/toolbar-modes.tsx | Add “Add component” dropdown (root-only, gated). |
| libs/@hashintel/petrinaut/src/ui/views/Editor/components/BottomBar/simulation-controls.tsx | Disable simulation controls when inside subnet view. |
| libs/@hashintel/petrinaut/src/ui/views/Editor/components/BottomBar/bottom-bar.tsx | Reset non-cursor edition modes when leaving edit; pass inSubnet. |
| libs/@hashintel/petrinaut/src/ui/petrinaut.tsx | Doc wording update. |
| libs/@hashintel/petrinaut/src/ui/petrinaut.stories.tsx | Add subnet-related story variants. |
| libs/@hashintel/petrinaut/src/ui/lib/compile-visualizer.ts | Visualizer token prop accepts typed values. |
| libs/@hashintel/petrinaut/src/ui/constants/ui-subviews.ts | Add nets subview; tighten typing with satisfies. |
| libs/@hashintel/petrinaut/src/ui/components/spreadsheet.stories.tsx | Spreadsheet story uses typed cell values. |
| libs/@hashintel/petrinaut/src/ui/components/arc-item.tsx | Arc list item supports label/color + endpoint IDs. |
| libs/@hashintel/petrinaut/src/react/state/user-settings-provider.tsx | Add action to set enableNetComponents. |
| libs/@hashintel/petrinaut/src/react/state/user-settings-context.ts | Add enableNetComponents setting + defaults. |
| libs/@hashintel/petrinaut/src/react/state/use-selection-cleanup.ts | Selection cleanup supports endpoint arc IDs + component instances. |
| libs/@hashintel/petrinaut/src/react/state/sdcpn-context.ts | Add componentInstance selection type; default SDCPN includes subnets/instances. |
| libs/@hashintel/petrinaut/src/react/state/editor-provider.tsx | Add add-component edition mode + componentSubnetId state; use active net for connections. |
| libs/@hashintel/petrinaut/src/react/state/editor-context.ts | Add add-component edition mode + action; store componentSubnetId. |
| libs/@hashintel/petrinaut/src/react/state/active-net-provider.tsx | New provider deriving active net from SDCPN + active subnet selection. |
| libs/@hashintel/petrinaut/src/react/state/active-net-context.ts | New context for active net definition + active subnet ID. |
| libs/@hashintel/petrinaut/src/react/simulation/provider.tsx | Minor comment tweak. |
| libs/@hashintel/petrinaut/src/react/sdcpn-provider.tsx | getItemType recognizes items across root + subnets + instances. |
| libs/@hashintel/petrinaut/src/react/petrinaut-provider.tsx | Wire in ActiveNetProvider. |
| libs/@hashintel/petrinaut/src/react/hooks/use-petrinaut-mutations.ts | Mutations inject targetSubnetId by default; subnets/instances mutations added. |
| libs/@hashintel/petrinaut/src/react/hooks/use-petrinaut-mutations.test.tsx | Update test context for new editor action. |
| libs/@hashintel/petrinaut/src/react/hooks/use-petrinaut-commands.ts | Commands inject targetSubnetId. |
| libs/@hashintel/petrinaut/src/react/hooks/use-petrinaut-commands.test.tsx | Update test context for new editor action. |
| libs/@hashintel/petrinaut/docs/petri-net-extensions.md | Doc update for typed kernel template insertion. |
| libs/@hashintel/petrinaut/docs/drawing-a-net.md | Document component ports + port arcs. |
| libs/@hashintel/petrinaut-core/src/types/selection.ts | Add componentInstance to selection union. |
| libs/@hashintel/petrinaut-core/src/types/sdcpn.ts | Add token typing, arc endpoints, subnets, component instances, ports. |
| libs/@hashintel/petrinaut-core/src/simulation/worker/simulation.worker.ts | Include discrete-values snapshot in frame payload. |
| libs/@hashintel/petrinaut-core/src/simulation/worker/frame-payload.ts | Add discreteValues to payload. |
| libs/@hashintel/petrinaut-core/src/simulation/runtime/simulation.ts | Flatten components for frame-store layout; merge parameter defaults. |
| libs/@hashintel/petrinaut-core/src/simulation/runtime/frame-store.ts | Thread discrete-values into frame readers. |
| libs/@hashintel/petrinaut-core/src/simulation/monte-carlo/transition-effect.ts | Decode typed inputs + encode typed outputs via codec. |
| libs/@hashintel/petrinaut-core/src/simulation/frames/frame-reader.ts | Decode typed token attributes using discrete-values snapshot. |
| libs/@hashintel/petrinaut-core/src/simulation/engine/types.ts | TokenRecord/typed kernel output + tokenValueCodec on simulation instance. |
| libs/@hashintel/petrinaut-core/src/simulation/engine/token-values.ts | New encode/decode/coercion helpers + TokenValueCodec. |
| libs/@hashintel/petrinaut-core/src/simulation/engine/execute-transitions.test.ts | Update tests for endpoint place IDs + codec presence. |
| libs/@hashintel/petrinaut-core/src/simulation/engine/compute-possible-transition.ts | Decode/encode typed tokens for transitions. |
| libs/@hashintel/petrinaut-core/src/simulation/engine/compute-possible-transition.test.ts | Add typed token encode/decode coverage. |
| libs/@hashintel/petrinaut-core/src/simulation/engine/check-transition-enablement.ts | Reject component-port endpoints in unflattened enablement checks. |
| libs/@hashintel/petrinaut-core/src/simulation/authoring/scenario/compile-scenario.ts | Coerce typed row values into TokenRecords. |
| libs/@hashintel/petrinaut-core/src/simulation/authoring/scenario/compile-scenario.test.ts | Add typed scenario row coercion coverage. |
| libs/@hashintel/petrinaut-core/src/simulation/authoring/metric/compile-metric.ts | Metrics receive typed TokenRecords. |
| libs/@hashintel/petrinaut-core/src/simulation/api.ts | Initial marking + frame-reader APIs use TokenRecord. |
| libs/@hashintel/petrinaut-core/src/schemas/scenario-schema.ts | Allow typed token row cell values in schema. |
| libs/@hashintel/petrinaut-core/src/schemas/metric-schema.ts | Update schema description for typed tokens. |
| libs/@hashintel/petrinaut-core/src/lsp/lib/checker.test.ts | LSP checks cover component-port typed inputs/outputs. |
| libs/@hashintel/petrinaut-core/src/lib/get-connections.ts | Connection graph supports endpoint arc IDs + component instances. |
| libs/@hashintel/petrinaut-core/src/layout/dimensions.ts | Add component instance dimensions for layout. |
| libs/@hashintel/petrinaut-core/src/layout/calculate-graph-layout.ts | Layout includes component instances + endpoint node IDs. |
| libs/@hashintel/petrinaut-core/src/index.ts | Export arc-endpoint helpers; export subnet/component schemas. |
| libs/@hashintel/petrinaut-core/src/handle/json-doc-handle/create-json-doc-handle.test.ts | Capabilities include subnets. |
| libs/@hashintel/petrinaut-core/src/file-format/types.ts | File format supports subnets + component instances. |
| libs/@hashintel/petrinaut-core/src/file-format/sdcpn-to-tikz.ts | TikZ export uses endpoint place IDs when possible. |
| libs/@hashintel/petrinaut-core/src/file-format/remove-visual-info.ts | Strip visual info from subnets/instances too. |
| libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.ts | Parse/fill visual info for subnets + instances. |
| libs/@hashintel/petrinaut-core/src/file-format/parse-sdcpn-file.test.ts | Import tests for subnets/instances + missing positions. |
| libs/@hashintel/petrinaut-core/src/extensions.test.ts | Extension logic covers component-port arcs + sanitization. |
| libs/@hashintel/petrinaut-core/src/default-codes.ts | Default code templates respect typed token attributes. |
| libs/@hashintel/petrinaut-core/src/commands.ts | Commands target active subnet; layout/paste support subnet targets. |
| libs/@hashintel/petrinaut-core/src/commands.test.ts | Tests for subnet paste/layout targeting. |
| libs/@hashintel/petrinaut-core/src/command-schemas.ts | Command schema accepts targetSubnetId. |
| libs/@hashintel/petrinaut-core/src/clipboard/serialize.ts | Clipboard serialization filters arcs via endpoint place IDs. |
| libs/@hashintel/petrinaut-core/src/clipboard/paste.ts | Clipboard paste maps arcs via endpoint place IDs. |
| libs/@hashintel/petrinaut-core/src/arc-endpoints.ts | New helpers for typed arc endpoints + keys. |
| libs/@hashintel/petrinaut-core/src/ai.ts | AI guidance updated for typed tokens + typed scenarios. |
| apps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx | Demo handle capabilities + “empty SDCPN” check includes subnets/scenarios. |
| .changeset/quiet-subnets-compose.md | Changeset for subnet/component support. |
| .changeset/h-6519-discrete-token-attribute-types.md | Changeset for discrete token attribute types. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ?.map((pl) => ({ | ||
| value: pl.id, | ||
| text: pl.name, | ||
| })) | ||
| .concat( |
| onKeyDown={(event) => { | ||
| if (event.key === "Enter" || event.key === " ") { | ||
| handleSelect(null); | ||
| } | ||
| }} |
eb0305c to
7a1a395
Compare
| snapshot(): EncodedDiscreteValues | undefined { | ||
| return this.#uuids.length > 0 ? [...this.#uuids] : undefined; | ||
| } |
| const userFn = compileUserCode<[TokenRecord[], ParameterValues]>( | ||
| code, | ||
| "Dynamics", | ||
| ) as UserDifferentialEquationFn; |
| const normalizedKey = event.key.toLowerCase(); | ||
| if (normalizedKey === "t" || normalizedKey === "1") { | ||
| event.preventDefault(); | ||
| updateCell(row, col, true); | ||
| setSelectedRow(null); | ||
| return; | ||
| } | ||
| if (normalizedKey === "f" || normalizedKey === "0") { | ||
| event.preventDefault(); | ||
| updateCell(row, col, false); | ||
| setSelectedRow(null); | ||
| return; | ||
| } | ||
| } |
| <Section | ||
| title="Dimensions" | ||
| tooltip="A type is an ordered tuple of real-valued dimensions. The index of each dimension determines its position in the token vector." | ||
| tooltip="A type is an ordered tuple of token attributes. Real attributes can be updated by dynamics; integer, boolean, and UUID attributes are discrete." |
UUID is more complex to integrate (128-bit values cannot live in one Float64 slot) and is split out to FE-1121. This also removes the UUID string-interning TokenValueCodec and the discreteValues frame payload plumbing, since integer and boolean encode directly as numbers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generated differential equation template returned a 0 derivative for integer/boolean attributes, contradicting the LSP Derivative type which marks discrete elements `?: never` (excess property checks do not fire through `tokens.map(...)`, so `never` is what enforces real-only derivatives). Freshly generated code showed a type error as a result. The template now returns derivatives for real elements only, and checker tests pin both the valid and invalid cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 19fa9d1. Configure here.
| }) as UserDifferentialEquationFn; | ||
| if (!type.elements.some((element) => element.type === "real")) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
Dynamics silently skipped without real
Medium Severity
When a place has dynamics enabled and linked differential-equation code but its colour type has no real elements, buildSimulation hits continue and never registers a compiled dynamics function. The run proceeds with no error while continuous dynamics for that place never execute.
Reviewed by Cursor Bugbot for commit 19fa9d1. Configure here.
| if (totalFrames > 0 && currentFrameReader) { | ||
| const placeTokenValues = currentFrameReader.getPlaceTokenValues(place.id); | ||
| if (!placeTokenValues) { | ||
| return <div className={messageStyle}>Place not found in frame</div>; | ||
| } | ||
|
|
||
| const tokenValues = Array.from(placeTokenValues.values); | ||
|
|
||
| for ( | ||
| let tokenIndex = 0; | ||
| tokenIndex < placeTokenValues.count; | ||
| tokenIndex++ | ||
| ) { | ||
| const token: Record<string, number> = {}; | ||
| for (let colIndex = 0; colIndex < dimensions; colIndex++) { | ||
| const dimensionName = placeType.elements[colIndex]!.name; | ||
| token[dimensionName] = | ||
| tokenValues[tokenIndex * dimensions + colIndex] ?? 0; | ||
| } | ||
| tokens.push(token); | ||
| } | ||
|
|
||
| tokens.push(...currentFrameReader.getPlaceTokens(place, placeType)); | ||
| parameters = mergeParameterValues(parameterValues, defaultParameterValues); | ||
| } else { |
| type="number" | ||
| step={ | ||
| columns[colIndex]?.type === "integer" | ||
| ? 1 | ||
| : undefined | ||
| } |
| role="checkbox" | ||
| aria-checked={Boolean(value)} | ||
| tabIndex={0} |
| <input | ||
| type="checkbox" | ||
| checked={Boolean(value)} | ||
| readOnly | ||
| tabIndex={-1} | ||
| className={booleanCellStyle} | ||
| /> |
| const values: number[] = []; | ||
| for (const elementName of outputPlace.elementNames) { | ||
| const rawValue = token[elementName]!; | ||
| for (const element of outputPlace.elements ?? []) { |
| for (const element of outputPlace.elements ?? []) { | ||
| let raw = token[element.name]; | ||
| if (isDistribution(raw)) { | ||
| if (element.type !== "real" && element.type !== "integer") { | ||
| throw new Error( |
Integer token attributes are stored as Float64 and rounded on read/write; the schema description now states the exact-integer range contract so authors know large values lose precision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>


🌟 What is the purpose of this PR?
Adds support for discrete token attribute types in Petrinaut, so coloured token dimensions can be represented as
integerorbooleanvalues in addition to continuousrealvalues.This updates the model schema, simulator runtime, code authoring surface, and editor UI so typed token values can be created, simulated, displayed, and passed into user-authored Petrinaut code consistently.
🔗 Related links
🔍 What does this change?
real,integer, andboolean.number | booleanvalues throughout Petrinaut core APIs.@hashintel/petrinautand@hashintel/petrinaut-core.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?
yarn workspace @hashintel/petrinaut-core test:unityarn workspace @hashintel/petrinaut test:unityarn exec turbo run lint:tsc --filter '@hashintel/petrinaut-core' --filter '@hashintel/petrinaut'yarn exec turbo run lint:eslint --filter '@hashintel/petrinaut-core' --filter '@hashintel/petrinaut'yarn exec oxfmt --check $(git diff --name-only --diff-filter=ACM) libs/@hashintel/petrinaut-core/src/simulation/engine/token-values.tsgit diff --check❓ How to test this?