Skip to content

FE-614: Support disabling SDCPN extensions#8763

Open
kube wants to merge 4 commits into
mainfrom
cf/fe-614-provide-prop-to-turn-onoff-sdcpn-extensions
Open

FE-614: Support disabling SDCPN extensions#8763
kube wants to merge 4 commits into
mainfrom
cf/fe-614-provide-prop-to-turn-onoff-sdcpn-extensions

Conversation

@kube
Copy link
Copy Markdown
Collaborator

@kube kube commented May 26, 2026

🌟 What is the purpose of this PR?

Allow Petrinaut document handles to declare which SDCPN extensions and global parameters are unavailable, so hosts such as CatCollab can embed Petrinaut around a bare Petri net without exposing HASH-specific extensions.

This introduces handle-level capabilities for read-only mode and extension availability, then adapts core mutation paths, runtime data paths, and the editor UI to respect those capabilities.

🔗 Related links

🔍 What does this change?

  • Adds PetrinautHandleCapabilities with readonly and disabledExtensions metadata.
  • Defines supported extension keys: colors, stochasticity, dynamics, and parameters.
  • Resolves effective instance capabilities from both host config and handle metadata.
  • Ensures dynamics is resolved as disabled whenever colors is disabled.
  • Exposes active extension settings through Petrinaut core and React context.
  • Makes JSON handles no-op direct changes when handle-level read-only mode is enabled.
  • Sanitizes mutations and paste commands so disabled extension/global parameter data is not introduced or retained.
  • Hides token type, differential equation, and global parameter navigation when unavailable.
  • Hides place color, dynamics, visualizer, transition result, stochastic, and parameter controls when unavailable.
  • Treats simulation scenarios and timelines as untyped when colors are disabled.
  • Ignores global parameter defaults and scenario overrides in simulation/experiment runtime paths when parameters are disabled.
  • Adds Petrinaut/ExtensionsDisabled Storybook coverage for a bare Petri net with SDCPN extensions and global parameters disabled.
  • Adds focused tests for read-only capabilities, extension sanitization, color/dynamics normalization, parameter disabling, and paste behavior.
  • Adds ANALYSIS.md in @hashintel/petrinaut describing touchpoints, tradeoffs, and follow-ups.

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

  • This does not implement CatCollabPetriNetHandle; it only adds the capability surface and UI/core behavior it can use later.
  • AI assistant tool schemas and prompts are not yet filtered by active extensions; core mutations still sanitize disabled extension data.

🐾 Next steps

  • Implement CatCollabPetriNetHandle once CatCollab's source model and edit semantics are finalized.
  • Decide whether additional Petri-net features should become capability flags for a stricter bare-net mode.
  • Consider more specific read-only reasons in the UI for handle-level read-only mode.

🛡 What tests cover this?

  • yarn lint:eslint in libs/@hashintel/petrinaut-core
  • yarn lint:tsc in libs/@hashintel/petrinaut-core
  • yarn test:unit --run in libs/@hashintel/petrinaut-core
  • turbo run lint:tsc --filter='@hashintel/petrinaut'
  • turbo run lint:eslint --filter='@hashintel/petrinaut'
  • turbo run test:unit --filter='@hashintel/petrinaut' -- --run
  • yarn lint:markdownlint libs/@hashintel/petrinaut/ANALYSIS.md .changeset/petrinaut-sdcpn-capabilities.md
  • git diff --check

❓ How to test this?

  1. Open Storybook and load Petrinaut/ExtensionsDisabled.
  2. Confirm token type, differential equation, global parameter, dynamics, visualizer, transition result, and stochastic controls are hidden.
  3. Create or use a Petrinaut handle with capabilities.disabledExtensions set to ['colors'] and confirm instance.extensions.dynamics resolves to false.
  4. Create or use a Petrinaut handle with capabilities.disabledExtensions set to ['parameters'] and confirm global parameter UI and simulation defaults are unavailable.
  5. Paste or mutate SDCPN data containing disabled extension fields and confirm the stored definition is sanitized.
  6. Set capabilities.readonly and confirm edit mutations are ignored and the editor reports read-only state.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 2026

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

Project Deployment Actions Updated (UTC)
hash Ready Ready Preview, Comment May 27, 2026 12:15am
petrinaut Ready Ready Preview, Comment May 27, 2026 12:15am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview May 27, 2026 12:15am

@cursor
Copy link
Copy Markdown

cursor Bot commented May 26, 2026

PR Summary

Medium Risk
Broad changes to mutation, paste, and simulation paths plus UI gating; behavior is capability-driven with tests, but hosts must understand sanitization and the colors→dynamics dependency.

Overview
Adds handle-level capabilities so Petrinaut hosts can embed a bare Petri net without HASH SDCPN extensions. Document handles may set readonly and disabledExtensions (colors, stochasticity, dynamics, parameters); omitting the list keeps today’s defaults.

Core: Resolves effective extensions on the instance (host readonly OR handle readonly; dynamics is forced off when colors is disabled). Mutations and clipboard paste sanitize the SDCPN so disabled data cannot be created or kept (e.g. stochastic → predicate, cleared kernels/types/equations/parameters). JSON handles ignore change() when handle-readonly.

UI: React context exposes extensions; sidebar, search, properties, firing-time, canvas, simulation settings/timeline, and experiments hide or neutralize color/dynamics/stochastic/parameter surfaces when unavailable.

Includes tests, Petrinaut/ExtensionsDisabled Storybook, ANALYSIS.md, and patch changesets for @hashintel/petrinaut / petrinaut-core.

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

@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 > hash.design Affects the `hash.design` design site (app) labels May 26, 2026
@augmentcode
Copy link
Copy Markdown

augmentcode Bot commented May 26, 2026

🤖 Augment PR Summary

Summary: Adds handle-level capability metadata so Petrinaut can run against “bare” Petri nets (e.g. CatCollab embeds) without exposing HASH SDCPN extensions.

Changes:

  • Introduced PetrinautHandleCapabilities (readonly, disabledExtensions) and extension keys (colors, stochasticity, dynamics).
  • Added core extension utilities to resolve capabilities, determine selection availability, and sanitize/strip disabled extension data.
  • Derived effective instance readonly + extensions from handle capabilities (and host readonly) and exposed them on the instance/context.
  • Updated mutation and paste paths to no-op or sanitize so disabled extension data can’t be introduced/retained.
  • Made JSON doc handles ignore change() when handle-level read-only is enabled.
  • Updated React/UI surfaces to hide token-type, dynamics, visualizer, transition-result, and stochastic controls when unavailable.
  • Adjusted simulation timeline/scenario views to treat places as untyped when colors are disabled.
  • Added focused unit tests for read-only behavior, mutation sanitization, and paste sanitization.
  • Added libs/@hashintel/petrinaut/ANALYSIS.md documenting touchpoints and follow-ups, plus a changeset.

🤖 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. 2 suggestions posted.

Fix All in Augment

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

disabled={isReadOnly}
tooltip={isReadOnly ? UI_MESSAGES.READ_ONLY_MODE : "Add token type"}
disabled={isDisabled}
tooltip={isDisabled ? UI_MESSAGES.READ_ONLY_MODE : "Add token type"}
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 26, 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/ui/views/Editor/panels/LeftSideBar/subviews/types-list.tsx:84 — When the button is disabled due to !extensions.colors, the tooltip uses UI_MESSAGES.READ_ONLY_MODE ("Editing is disabled"), which can mislead since the editor may still be writable. Consider using a capability-specific message for the extension-unavailable state (also applies to .../differential-equations-list.tsx:42).

Severity: low

Other Locations
  • libs/@hashintel/petrinaut/src/ui/views/Editor/panels/LeftSideBar/subviews/differential-equations-list.tsx:42

Fix This in Augment

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

extensions: {
colors: !disabled.has("colors"),
stochasticity: !disabled.has("stochasticity"),
dynamics: !disabled.has("dynamics"),
Copy link
Copy Markdown

@augmentcode augmentcode Bot May 26, 2026

Choose a reason for hiding this comment

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

libs/@hashintel/petrinaut-core/src/extensions.ts:54resolvePetrinautHandleCapabilities() can return { colors: false, dynamics: true }, but elsewhere dynamics is treated as effectively gated by extensions.colors && extensions.dynamics. That mismatch could confuse consumers treating extensions as the effective availability surface; consider normalizing extensions.dynamics (or exposing a derived canUseDynamics) to reflect the effective behavior.

Severity: medium

Fix This in Augment

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

change(fn) {
if (capabilities?.readonly) {
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Handle change skips extension sanitization

Medium Severity

The change method in createJsonDocHandle only respects readonly capabilities. It doesn't account for disabledExtensions, allowing direct edits to introduce or retain extension-specific data (e.g., types, equations) that should otherwise be unavailable or stripped.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0ac1966. Configure here.

mutate,
() => definition.get(),
capabilities.extensions,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No sanitize on instance create

High Severity

The createPetrinaut function's definition store exposes the document without sanitizing data for disabled extensions. This allows initial or loaded SDCPNs to retain types, stochastic transitions, or dynamics, causing inconsistencies between UI capabilities and the underlying data until a mutation eventually applies stripDisabledExtensionData.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0ac1966. Configure here.

sanitizeTransitionForExtensions(transition, extensions),
);
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Strip ignores scenario initial state

Medium Severity

stripDisabledExtensionData clears types, differential equations, and place or transition extension fields, but leaves scenarios (and their initialState payloads) untouched. After the first guarded mutation with colors disabled, the net can have no types while scenarios still describe colored token matrices for places, producing an inconsistent SDCPN for simulation and scenario UI.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0ac1966. Configure here.

fn(sdcpn);
stripDisabledExtensionData(sdcpn, extensions);
});
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Undo restores stripped extensions

Medium Severity

stripDisabledExtensionData runs in the same handle.change transaction as the user’s edit. Undo applies inverse patches for the whole transaction, so extension data removed during sanitization can reappear in the stored document after undo even though capabilities still disable those extensions.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0ac1966. Configure here.

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.

There are 7 total unresolved issues (including 4 from previous reviews).

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 7194bc7. Configure here.

try {
sim = await createSimulation({
sdcpn,
sdcpn: simulationSdcpn,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Simulation ignores disabled extensions

High Severity

When SDCPN extensions are disabled, the SimulationProvider only sanitizes global parameters before passing the definition to the simulation engine. Other disabled extensions, such as colors, stochasticity, dynamics, and token types, are not removed. This can cause the simulation to use data from disabled features, leading to behavior that doesn't align with the UI.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7194bc7. Configure here.

stripDisabledExtensionData(sdcpn, extensions);
newItemIds = result.newItemIds.filter((item) =>
isSelectionTypeAvailableForExtensions(item.type, extensions),
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto layout skips extension strip

Medium Severity

The applyAutoLayout command uses a direct mutate call and doesn't invoke stripDisabledExtensionData. This can leave data from disabled extensions in the document, unlike other mutation paths (like clipboard paste) that now correctly remove it.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7194bc7. Configure here.


return {
id,
capabilities,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Undo restores disabled extension data

Medium Severity

History undo and redo replay Immer patches without re-applying extension sanitization. Stepping back to an earlier checkpoint can restore token types, stochastic transitions, parameters, and other fields that guarded mutations had stripped for the current capability set.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7194bc7. Configure here.

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/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