Skip to content

FE-522: Add subnet composition support to Petrinaut#8662

Draft
kube wants to merge 1 commit into
mainfrom
cf/fe-522-basic-support-for-subnets-component-based-net-composition
Draft

FE-522: Add subnet composition support to Petrinaut#8662
kube wants to merge 1 commit into
mainfrom
cf/fe-522-basic-support-for-subnets-component-based-net-composition

Conversation

@kube
Copy link
Copy Markdown
Collaborator

@kube kube commented Apr 28, 2026

🌟 What is the purpose of this PR?

Adds initial subnet composition support to Petrinaut so reusable subnet definitions can be authored in core and instantiated in the editor.

This introduces component instances and wiring declarations for merging a parent-net place with a port place inside a subnet.

🔗 Related links

🔍 What does this change?

  • Adds subnet, component instance, wire, and Place.isPort types to @hashintel/petrinaut-core.
  • Adds Zod schemas, AI/tool exports, and mutation actions for creating/updating/removing subnets, component instances, and wires.
  • Adds targetSubnetId support to net-local mutations so places, transitions, arcs, types, parameters, and equations can be edited inside the active subnet.
  • Updates clipboard paste, auto-layout, file import/export parsing, visual-info stripping/filling, graph layout, selection, and connection highlighting for subnets/component instances/wires.
  • Adds ActiveNetProvider and active-net-aware editor state, lists, selection cleanup, keyboard shortcuts, commands, and mutations.
  • Adds a Nets sidebar for switching between root and subnet definitions.
  • Adds component instance nodes, dashed wire edges, add-component toolbar mode, and cursor tooltip behavior in ReactFlow.
  • Adds a component port toggle to place properties and component instance properties panel support.
  • Adds a Hospital Network with Subnet bundled example.
  • Adds ANALYSIS.md documenting implementation notes and follow-up questions.

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:

  • are in a state where docs changes are not yet required but will be

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

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • Wires are currently authoring-level merge declarations: they are modeled, validated, rendered, imported/exported, and editable.
  • Simulation flattening/composition for component instances is not implemented in this PR.

🐾 Next steps

  • Design executable subnet composition/flattening semantics for simulation.
  • Decide whether nested component placement should be exposed in the editor UI.

🛡 What tests cover this?

  • New core action tests for subnet-targeted mutations and component wiring validation.
  • New core command tests for subnet-targeted clipboard paste and auto-layout.
  • New file-format tests for subnets, component instances, wires, and missing component positions.
  • Existing clipboard, React command/mutation hook, lint, typecheck, and package build coverage.

❓ How to test this?

  1. Open Petrinaut and load the Hospital Network with Subnet example.
  2. Use the Nets sidebar to switch between the root net and the ER triage subnet.
  3. Mark subnet places as component ports from place properties.
  4. In the root net, use the component add mode to place a subnet instance.
  5. Connect a root place to a component port and confirm a dashed wire edge is created.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 28, 2026

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

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment May 26, 2026 11:54pm
petrinaut Ready Ready Preview, Comment May 26, 2026 11:54pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview May 26, 2026 11:54pm

@cursor
Copy link
Copy Markdown

cursor Bot commented Apr 28, 2026

PR Summary

Medium Risk
Introduces new SDCPN data structures (subnets, componentInstances, wiring) and routes most editor mutations/rendering through an ActiveNet abstraction, which can affect many core edit flows and file import/export compatibility.

Overview
Adds basic subnet-based composition to Petrinaut by extending the SDCPN schema with subnets, componentInstances, port places (Place.isPort), and wiring, plus a new hospitalNetwork example demonstrating the format.

Introduces an ActiveNetProvider to switch the editor between the root net and a selected subnet, updating sidebar lists, diagnostics, selection cleanup, parameter defaults, graph layout, and mutation helpers to operate on the active net.

Adds UI for managing/viewing nets (new Nets sidebar subview), creating component instances from the toolbar (add-component mode + cursor tooltip), rendering component instance nodes with port handles, and drawing dashed wire edges; selection/properties support is extended to componentInstance items. File import/remove-visual-info and Zod schemas are updated to handle subnets/component instances and their positions.

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

@github-actions github-actions Bot added area/libs Relates to first-party libraries/crates/packages (area) type/eng > frontend Owned by the @frontend team labels Apr 28, 2026
@kube kube marked this pull request as draft April 28, 2026 10:16
@kube kube changed the title Cf/fe 522 basic support for subnets component based net composition Subnets/Components and Composition Apr 28, 2026
@augmentcode
Copy link
Copy Markdown

augmentcode Bot commented Apr 28, 2026

🤖 Augment PR Summary

Summary: This draft PR explores “subnets” and component-based composition in Petrinaut by introducing a notion of an “active net” (root vs selected subnet) and rendering subnet instances as nodes with wiring.

Changes:

  • Extends the SDCPN type model with Subnet, ComponentInstance, Wire, and a Place.isPort flag.
  • Updates the file format schema and import/export helpers to include subnets/component instances and to fill/strip their visual positioning data.
  • Adds ActiveNetContext/ActiveNetProvider and migrates multiple UI panels/hooks to operate on the active net’s places/transitions/types/etc.
  • Introduces a “Nets” sidebar subview to switch between root and subnets, plus toolbar support for “add component instance” mode.
  • Enhances mutation helpers to target the active net (root or subnet) and adds CRUD helpers for subnets/component instances.
  • Adds ReactFlow rendering for component instance nodes (with port handles) and dashed “wire” edges, plus a cursor-following tooltip for add modes.
  • Adds a new “Hospital Network” example demonstrating a root net with an ER triage subnet and a component instance wired to ports.

Technical Notes: Graph layout and selection cleanup now operate on the active net; getItemType was broadened to search both root and all subnets for IDs.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed. 4 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

@@ -75,12 +89,13 @@ export const MutationProvider: React.FC<MutationProviderProps> = ({
},
removePlace(placeId) {
Copy link
Copy Markdown

@augmentcode augmentcode Bot Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libs/@hashintel/petrinaut/src/state/mutation-provider.tsx:L90 — When a place is removed from a net, any componentInstances[].wiring entries that reference it (as externalPlaceId, or indirectly via a port ID) aren’t cleaned up, which can leave dangling wire edges/handles in the ReactFlow view. This same issue can also occur when places are deleted via deleteItemsByIds (wires aren’t considered there either).

Severity: medium

Other Locations
  • libs/@hashintel/petrinaut/src/state/mutation-provider.tsx:403

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

sdcpn.subnets = subnets;
});
},
removeSubnet(subnetId) {
Copy link
Copy Markdown

@augmentcode augmentcode Bot Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libs/@hashintel/petrinaut/src/state/mutation-provider.tsx:L389 — removeSubnet removes the subnet definition but doesn’t address any componentInstances (in the root net or other nets) that reference that subnetId, which can leave instances/wires pointing at non-existent ports. Consider either preventing subnet deletion while referenced or cleaning up dependent instances/wiring to avoid inconsistent state.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

);
const rafRef = useRef(0);

useEffect(() => {
Copy link
Copy Markdown

@augmentcode augmentcode Bot Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libs/@hashintel/petrinaut/src/views/SDCPN/components/cursor-tooltip.tsx:L40 — This useEffect attaches a global mousemove listener and calls setPosition(...) on every mouse move even when not in an add mode (when the tooltip returns null). That can cause unnecessary re-renders across long sessions; consider only subscribing/updating while label is non-null (i.e., when the tooltip would actually render).

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

};
};

/**
Copy link
Copy Markdown

@augmentcode augmentcode Bot Apr 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libs/@hashintel/petrinaut/src/core/types/sdcpn.ts:L117 — The JSDoc for “An instance of a subnet…” currently sits above Wire, so it looks like it’s documenting the wrong type (and the Wire docblock follows immediately after). Consider moving that first docblock onto ComponentInstance to avoid misleading generated docs/tooltips.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 01705ea. Configure here.

type === "place" ||
type === "transition" ||
type === "componentInstance"
) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Component instances treated as non-canvas items in selection

Medium Severity

The hasNonCanvasItems check (around line 86) only considers place, transition, and arc as canvas item types. Since this PR adds componentInstance as a new canvas node type, any selected component instance is incorrectly treated as a "non-canvas" item. This causes the selection base to be cleared when a component instance is in the selection and the user interacts with canvas selection, breaking multi-select behavior for component instances.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 01705ea. Configure here.

},
commitNodePositions(commits) {
guardedMutate((sdcpn) => {
const net = resolveNet(sdcpn);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paste always targets root net, ignoring active subnet

Medium Severity

pasteEntities calls pasteFromClipboard(mutatePetriNetDefinition) which directly mutates the root SDCPN's places, transitions, etc. arrays. Unlike all other mutation functions in this provider, it does not use resolveNet to target the active net. When a user is viewing a subnet and pastes, entities are incorrectly added to the root net instead of the active subnet.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 01705ea. Configure here.

break;
}
}
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing subnet leaves dangling component instance references

Medium Severity

removeSubnet splices the subnet from the array but does not clean up componentInstances in the root net (or other subnets) whose subnetId references the deleted subnet. Other removal functions like removeType and removeDifferentialEquation follow a pattern of clearing dangling references, but removeSubnet does not, leaving orphaned component instances pointing to a non-existent subnet.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 01705ea. Configure here.

@kube kube force-pushed the cf/fe-522-basic-support-for-subnets-component-based-net-composition branch from 01705ea to 0e89ed3 Compare May 26, 2026 23:46
@github-actions github-actions Bot added the area/infra Relates to version control, CI, CD or IaC (area) label May 26, 2026
@kube kube changed the title Subnets/Components and Composition FE-522: Add subnet composition support to Petrinaut May 26, 2026
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.

1 participant