Skip to content

Conversation

@CiaranMn
Copy link
Member

@CiaranMn CiaranMn commented Aug 27, 2025

🌟 What is the purpose of this PR?

We want to use the Petri net editor in other projects/repos, as well as making it place nicely with change management systems that require changes by mutation rather than creating data with new identity (e.g. Automerge).

  1. Extracts the Petri net editor into its own standalone package so that it can be published
  2. Updates the editor so that it creates updates to the Petri net by mutation.
  3. Updates other packages it depends on (directly or indirectly) so that they can be published, e.g.
    • remove references to local non-published dependencies.
    • remove @blockprotocol/core patch (I have published a version that includes the patch)
    • update prepublish script to handle more packages, or add specific prepublish scripts where packages need special handling
  4. I have published all relevant packages

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • modifies npm-publishable libraries and I have published them

📜 Does this require a change to the docs?

The changes in this PR:

  • are internal and do not require a docs change

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

The changes in this PR:

  • do not affect the execution graph

CiaranMn added 30 commits August 6, 2025 19:46
@CiaranMn CiaranMn requested a review from vilkinsons September 30, 2025 13:37
@CiaranMn CiaranMn marked this pull request as ready for review September 30, 2025 13:38
@CiaranMn CiaranMn requested a review from a team as a code owner September 30, 2025 13:38
}

return false;
}, [arcs, nodes, tokenTypes, persistedNet, title, parentProcess]);
Copy link

Choose a reason for hiding this comment

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

The persistToGraph function lacks error handling, which means if any of the database operations fail, the persistPending state will remain true forever, leaving the UI in a broken state.

View Details
📝 Patch Details
diff --git a/apps/hash-frontend/src/pages/process.page/process-editor-wrapper/use-process-save-and-load.tsx b/apps/hash-frontend/src/pages/process.page/process-editor-wrapper/use-process-save-and-load.tsx
index 68ddde592..366220c70 100644
--- a/apps/hash-frontend/src/pages/process.page/process-editor-wrapper/use-process-save-and-load.tsx
+++ b/apps/hash-frontend/src/pages/process.page/process-editor-wrapper/use-process-save-and-load.tsx
@@ -219,7 +219,8 @@ export const useProcessSaveAndLoad = ({
 
     setPersistPending(true);
 
-    let persistedEntityId = selectedNetId;
+    try {
+      let persistedEntityId = selectedNetId;
 
     if (selectedNetId) {
       await updateEntity({
@@ -591,11 +592,12 @@ export const useProcessSaveAndLoad = ({
       }
     }
 
-    await refetchPersistedNets({ updatedEntityId: persistedEntityId });
-    setSelectedNetId(persistedEntityId);
-    setUserEditable(true);
-
-    setPersistPending(false);
+      await refetchPersistedNets({ updatedEntityId: persistedEntityId });
+      setSelectedNetId(persistedEntityId);
+      setUserEditable(true);
+    } finally {
+      setPersistPending(false);
+    }
   }, [
     activeWorkspaceWebId,
     archiveEntity,

Analysis

persistToGraph function lacks error handling causing UI to break on database failures

What fails: persistToGraph function in use-process-save-and-load.tsx has no try-catch around database operations (updateEntity, createEntity, archiveEntity), leaving persistPending state stuck at true when mutations fail

How to reproduce:

// Any database operation failure (network error, permission denied, etc.) in persistToGraph
// For example: network disconnection during updateEntity call at line 225

Result: setPersistPending(false) at line 598 never executes, edit bar permanently hidden (isDirty && !persistPending condition fails), user cannot save again

Expected: setPersistPending(false) should execute in finally block to restore UI functionality regardless of database operation success/failure

Fix applied: Wrapped function body in try-finally block ensuring setPersistPending(false) always executes per Apollo error handling docs

@CiaranMn CiaranMn added this pull request to the merge queue Sep 30, 2025
Merged via the queue into main with commit d442ca6 Sep 30, 2025
159 checks passed
@CiaranMn CiaranMn deleted the cm/extract-petri-net-editor-into-package branch September 30, 2025 15:21
@github-actions
Copy link
Contributor

Benchmark results

@rust/hash-graph-benches – Integrations

policy_resolution_extra_large

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 10002 $$99.3 \mathrm{ms} \pm 585 \mathrm{μs}\left({\color{gray}-1.589 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$5.71 \mathrm{ms} \pm 28.8 \mathrm{μs}\left({\color{gray}-0.213 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 5001 $$78.1 \mathrm{ms} \pm 529 \mathrm{μs}\left({\color{gray}-1.850 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 27604 $$292 \mathrm{ms} \pm 1.09 \mathrm{ms}\left({\color{gray}-0.719 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$22.6 \mathrm{ms} \pm 208 \mathrm{μs}\left({\color{gray}4.64 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 13450 $$231 \mathrm{ms} \pm 798 \mathrm{μs}\left({\color{gray}0.007 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 11308 $$462 \mathrm{ms} \pm 2.31 \mathrm{ms}\left({\color{red}228 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$6.76 \mathrm{ms} \pm 32.3 \mathrm{μs}\left({\color{gray}1.46 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 5628 $$101 \mathrm{ms} \pm 7.24 \mathrm{ms}\left({\color{red}12.2 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_large

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2002 $$29.1 \mathrm{ms} \pm 165 \mathrm{μs}\left({\color{gray}-0.475 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.57 \mathrm{ms} \pm 17.8 \mathrm{μs}\left({\color{gray}-0.464 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1001 $$13.4 \mathrm{ms} \pm 85.2 \mathrm{μs}\left({\color{gray}0.441 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 3314 $$39.3 \mathrm{ms} \pm 272 \mathrm{μs}\left({\color{gray}-0.244 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$13.9 \mathrm{ms} \pm 75.2 \mathrm{μs}\left({\color{gray}0.482 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 1526 $$25.0 \mathrm{ms} \pm 125 \mathrm{μs}\left({\color{gray}-0.392 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 2078 $$29.5 \mathrm{ms} \pm 182 \mathrm{μs}\left({\color{lightgreen}-38.329 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.83 \mathrm{ms} \pm 22.2 \mathrm{μs}\left({\color{gray}-1.695 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 1033 $$14.7 \mathrm{ms} \pm 105 \mathrm{μs}\left({\color{lightgreen}-38.974 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_medium

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 102 $$4.02 \mathrm{ms} \pm 19.1 \mathrm{μs}\left({\color{gray}1.95 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$3.09 \mathrm{ms} \pm 12.4 \mathrm{μs}\left({\color{gray}0.725 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 51 $$3.47 \mathrm{ms} \pm 16.0 \mathrm{μs}\left({\color{gray}0.137 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 269 $$5.41 \mathrm{ms} \pm 25.7 \mathrm{μs}\left({\color{gray}-0.132 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$3.69 \mathrm{ms} \pm 17.1 \mathrm{μs}\left({\color{gray}0.442 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 107 $$4.26 \mathrm{ms} \pm 23.6 \mathrm{μs}\left({\color{gray}-0.692 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 133 $$4.66 \mathrm{ms} \pm 19.6 \mathrm{μs}\left({\color{gray}-0.365 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$3.52 \mathrm{ms} \pm 14.7 \mathrm{μs}\left({\color{gray}-0.071 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 63 $$4.15 \mathrm{ms} \pm 18.8 \mathrm{μs}\left({\color{gray}-0.072 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_none

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 2 $$2.55 \mathrm{ms} \pm 11.7 \mathrm{μs}\left({\color{red}5.58 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.43 \mathrm{ms} \pm 10.1 \mathrm{μs}\left({\color{gray}4.04 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 1 $$2.50 \mathrm{ms} \pm 11.3 \mathrm{μs}\left({\color{gray}2.22 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 8 $$2.80 \mathrm{ms} \pm 11.1 \mathrm{μs}\left({\color{red}6.47 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$2.61 \mathrm{ms} \pm 13.0 \mathrm{μs}\left({\color{gray}4.20 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 3 $$2.84 \mathrm{ms} \pm 13.2 \mathrm{μs}\left({\color{gray}4.49 \mathrm{\%}}\right) $$ Flame Graph

policy_resolution_small

Function Value Mean Flame graphs
resolve_policies_for_actor user: empty, selectivity: high, policies: 52 $$3.04 \mathrm{ms} \pm 10.8 \mathrm{μs}\left({\color{red}8.36 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: low, policies: 1 $$2.70 \mathrm{ms} \pm 12.3 \mathrm{μs}\left({\color{red}8.16 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: empty, selectivity: medium, policies: 25 $$2.89 \mathrm{ms} \pm 13.4 \mathrm{μs}\left({\color{red}10.5 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: high, policies: 94 $$3.41 \mathrm{ms} \pm 16.5 \mathrm{μs}\left({\color{red}8.40 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: low, policies: 1 $$2.92 \mathrm{ms} \pm 10.2 \mathrm{μs}\left({\color{red}7.40 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: seeded, selectivity: medium, policies: 26 $$3.16 \mathrm{ms} \pm 12.3 \mathrm{μs}\left({\color{red}8.00 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: high, policies: 66 $$3.31 \mathrm{ms} \pm 16.9 \mathrm{μs}\left({\color{red}8.08 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: low, policies: 1 $$2.90 \mathrm{ms} \pm 11.1 \mathrm{μs}\left({\color{red}7.98 \mathrm{\%}}\right) $$ Flame Graph
resolve_policies_for_actor user: system, selectivity: medium, policies: 29 $$3.17 \mathrm{ms} \pm 15.3 \mathrm{μs}\left({\color{red}8.60 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity

Function Value Mean Flame graphs
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1 $$31.4 \mathrm{ms} \pm 324 \mathrm{μs}\left({\color{gray}0.687 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1 $$31.7 \mathrm{ms} \pm 365 \mathrm{μs}\left({\color{gray}3.72 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1 $$30.5 \mathrm{ms} \pm 299 \mathrm{μs}\left({\color{gray}-0.967 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1 $$30.4 \mathrm{ms} \pm 283 \mathrm{μs}\left({\color{gray}-1.489 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2 $$30.8 \mathrm{ms} \pm 252 \mathrm{μs}\left({\color{gray}1.35 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1 $$31.5 \mathrm{ms} \pm 288 \mathrm{μs}\left({\color{gray}3.62 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1 $$31.8 \mathrm{ms} \pm 305 \mathrm{μs}\left({\color{gray}3.91 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1 $$30.4 \mathrm{ms} \pm 331 \mathrm{μs}\left({\color{gray}-0.543 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1 $$30.9 \mathrm{ms} \pm 317 \mathrm{μs}\left({\color{gray}1.32 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity_type

Function Value Mean Flame graphs
get_entity_type_by_id Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba $$9.44 \mathrm{ms} \pm 35.3 \mathrm{μs}\left({\color{gray}0.659 \mathrm{\%}}\right) $$ Flame Graph

representative_read_multiple_entities

Function Value Mean Flame graphs
entity_by_property depths: DT=0, PT=0, ET=0, E=0 $$58.5 \mathrm{ms} \pm 291 \mathrm{μs}\left({\color{gray}0.503 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=0, PT=0, ET=0, E=2 $$66.8 \mathrm{ms} \pm 281 \mathrm{μs}\left({\color{gray}0.527 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=0, PT=0, ET=2, E=2 $$76.6 \mathrm{ms} \pm 345 \mathrm{μs}\left({\color{gray}1.34 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=0, PT=2, ET=2, E=2 $$85.9 \mathrm{ms} \pm 348 \mathrm{μs}\left({\color{gray}-0.041 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=2, PT=2, ET=2, E=2 $$92.8 \mathrm{ms} \pm 271 \mathrm{μs}\left({\color{gray}-1.484 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=255, PT=255, ET=255, E=255 $$116 \mathrm{ms} \pm 344 \mathrm{μs}\left({\color{gray}-0.281 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=0, ET=0, E=0 $$52.0 \mathrm{ms} \pm 300 \mathrm{μs}\left({\color{gray}1.26 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=0, ET=0, E=2 $$97.9 \mathrm{ms} \pm 379 \mathrm{μs}\left({\color{gray}0.635 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=0, ET=2, E=2 $$111 \mathrm{ms} \pm 339 \mathrm{μs}\left({\color{gray}0.045 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=2, ET=2, E=2 $$121 \mathrm{ms} \pm 386 \mathrm{μs}\left({\color{gray}-0.209 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=2, PT=2, ET=2, E=2 $$129 \mathrm{ms} \pm 398 \mathrm{μs}\left({\color{gray}0.214 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=255, PT=255, ET=255, E=255 $$150 \mathrm{ms} \pm 437 \mathrm{μs}\left({\color{gray}-0.475 \mathrm{\%}}\right) $$ Flame Graph

scaling_read_entity_complete_one_depth

Function Value Mean Flame graphs
entity_by_id 1 entities $$74.5 \mathrm{ms} \pm 211 \mathrm{μs}\left({\color{gray}-2.080 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$256 \mathrm{ms} \pm 1.91 \mathrm{ms}\left({\color{gray}-0.731 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 25 entities $$137 \mathrm{ms} \pm 535 \mathrm{μs}\left({\color{gray}0.105 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 5 entities $$126 \mathrm{ms} \pm 2.11 \mathrm{ms}\left({\color{gray}1.86 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 50 entities $$360 \mathrm{ms} \pm 1.76 \mathrm{ms}\left({\color{gray}2.99 \mathrm{\%}}\right) $$ Flame Graph

scaling_read_entity_complete_zero_depth

Function Value Mean Flame graphs
entity_by_id 1 entities $$15.2 \mathrm{ms} \pm 48.0 \mathrm{μs}\left({\color{gray}2.11 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$15.0 \mathrm{ms} \pm 85.9 \mathrm{μs}\left({\color{gray}2.08 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 25 entities $$15.3 \mathrm{ms} \pm 75.3 \mathrm{μs}\left({\color{gray}0.577 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 5 entities $$15.0 \mathrm{ms} \pm 96.0 \mathrm{μs}\left({\color{gray}1.20 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 50 entities $$16.8 \mathrm{ms} \pm 49.6 \mathrm{μs}\left({\color{lightgreen}-10.710 \mathrm{\%}}\right) $$ Flame Graph

scaling_read_entity_linkless

Function Value Mean Flame graphs
entity_by_id 1 entities $$15.3 \mathrm{ms} \pm 76.9 \mathrm{μs}\left({\color{gray}1.47 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$14.9 \mathrm{ms} \pm 66.7 \mathrm{μs}\left({\color{gray}-0.471 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 100 entities $$14.9 \mathrm{ms} \pm 72.7 \mathrm{μs}\left({\color{gray}1.56 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 1000 entities $$15.4 \mathrm{ms} \pm 75.8 \mathrm{μs}\left({\color{gray}1.21 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10000 entities $$23.5 \mathrm{ms} \pm 156 \mathrm{μs}\left({\color{gray}1.84 \mathrm{\%}}\right) $$ Flame Graph

scenarios

Function Value Mean Flame graphs
full_test query-limited $$136 \mathrm{ms} \pm 556 \mathrm{μs}\left({\color{red}8.22 \mathrm{\%}}\right) $$ Flame Graph
full_test query-unlimited $$132 \mathrm{ms} \pm 551 \mathrm{μs}\left({\color{gray}3.56 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-limited $$43.4 \mathrm{ms} \pm 248 \mathrm{μs}\left({\color{lightgreen}-59.826 \mathrm{\%}}\right) $$ Flame Graph
linked_queries query-unlimited $$595 \mathrm{ms} \pm 925 \mathrm{μs}\left({\color{gray}2.98 \mathrm{\%}}\right) $$ Flame Graph

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

Labels

area/apps > hash.design Affects the `hash.design` design site (app) area/apps > hash* Affects HASH (a `hash-*` app) area/apps > hash-api Affects the HASH API (app) area/apps area/blocks Relates to first-party blocks (area) area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area) area/libs Relates to first-party libraries/crates/packages (area) area/tests > integration New or updated integration tests area/tests > playwright New or updated Playwright tests area/tests New or updated tests type/eng > backend Owned by the @backend team type/eng > frontend Owned by the @frontend team type/legal Owned by the @legal team

Development

Successfully merging this pull request may close these issues.

3 participants