Skip to content

FE-1237: Add optional per-place token capacity - #9177

Open
kube wants to merge 2 commits into
cf/fe-1314-run-experiment-runs-in-parallel-across-workersfrom
cf/fe-1237-place-token-capacity
Open

FE-1237: Add optional per-place token capacity#9177
kube wants to merge 2 commits into
cf/fe-1314-run-experiment-runs-in-parallel-across-workersfrom
cf/fe-1237-place-token-capacity

Conversation

@kube

@kube kube commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Adds an optional per-place token capacity, set from the place properties panel. Dora Ma requested it for supply-chain models with finite storage. It also converts frames from growable to fixed-size, which the GPU backend (#9179) requires.

This PR sits above #9162 in stack #9280, and #9178 builds on it.

🔗 Related links

🚫 Blocked by

🔍 What does this change?

A transition cannot fire if firing would take an output place above its capacity. This is the standard Petri-net capacity constraint.

{ id: "p_buffer", name: "Buffer", capacity: 20 }
  • Output tokens apply at the end of a frame, and the check counts what earlier transitions in the same frame already committed, so several transitions feeding one capped place cannot collectively overflow it.
  • Deadlock detection uses the same check, so a net blocked only by full output places reports as deadlocked instead of stepping to maxTime.
  • buildSimulation rejects an initial marking that exceeds a place's capacity, with a message naming the place.
  • A net that declares no capacities gets empty constraint tables, and the hot path skips them.

Review fixes

  • The interactive stepping path (executeTransitions) now threads a pending-output tally into the capacity check, so several transitions feeding one capped place in a single step can no longer collectively overflow it. This matches the Monte Carlo path and the behaviour docs/simulation.md already promised. A regression test in compute-next-frame.test.ts pins it: two producers feed one capacity-1 place, and exactly one fires.
  • The initial-marking check in buildSimulation now normalizes the capacity the same way the runtime does, so a malformed capacity that runs unbounded cannot reject a marking.
  • The capacity NumberInput gained a max of 4294967294, one below the engine's unbounded sentinel.
  • The capacity checkbox's comment claimed the value defaults to the current token count. It now states the real behaviour: the capacity starts at 1, and a marking above the cap fails the build with a message naming the place.

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:

  • require changes to docs which are made as part of this PR

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

The changes in this PR:

  • do not affect the execution graph

🐾 Next steps

Build the fixed-size frame layout (SoA over WASM linear memory). The runtime still uses growable frames.

🛡 What tests cover this?

  • engine/capacity.test.ts covers the constraint tables and the headroom check.
  • monte-carlo/capacity.test.ts covers end-of-frame accumulation across several transitions feeding one place, and deadlock reporting.
  • engine/compute-next-frame.test.ts covers the interactive stepping path holding a capacity when two transitions feed one place in a single step.

❓ How to test this?

  1. Select a place, switch on Token capacity, and set a small value.
  2. Run the simulation. Transitions feeding that place stop firing at the cap, and the run reports as deadlocked once nothing else can fire.

🤖 Generated with Claude Code

@vercel

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

@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 8, 2026
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 6a7dd72 to 367c694 Compare August 13, 2026 08:20
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 367c694 to f78b501 Compare August 14, 2026 23:53
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from f78b501 to 03a799f Compare August 15, 2026 00:51
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 03a799f to 9985050 Compare August 18, 2026 00:05
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 9985050 to 6e5226a Compare August 18, 2026 00:33
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 6e5226a to 3c8e832 Compare August 18, 2026 00:47
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 5960041 to 63a72c6 Compare August 18, 2026 23:19
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 63a72c6 to 1259e63 Compare August 18, 2026 23:37
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 1259e63 to c3bb82d Compare August 19, 2026 09:03
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from c3bb82d to 5c74426 Compare August 19, 2026 09:40
@kube
kube force-pushed the cf/fe-1237-place-token-capacity branch from 5c74426 to ba9db1e Compare August 19, 2026 10:05
@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes simulation enablement and deadlock on both interactive and Monte Carlo engines. Nets without capacities keep empty constraint tables, but incorrect headroom logic would alter firing and completion.

Overview
Adds optional per-place token capacity as a Petri-net enablement constraint: a transition cannot fire if the net change would take any output place over its cap. Self-loops are not blocked; read/inhibitor arcs do not free room. Output is applied at end of frame, so pending counts stop several producers from jointly overflowing one place.

buildSimulation rejects an initial marking above capacity. Deadlock detection includes the same check so a net blocked only by full outputs stops instead of running to maxTime. Uncapped nets get empty constraint tables and skip the hot path.

The place properties panel can enable a cap (default 1). Docs and tests cover constraint tables, Monte Carlo stepping, and interactive two-producer overflow. Frames remain growable; capacity is not in the HIR fingerprint.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

// Default to the current token count so enabling the limit
// cannot retroactively invalidate the initial marking.
update: { capacity: checked === true ? 1 : null },
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Capacity default ignores token count

Medium Severity

Enabling Token capacity always writes capacity: 1, while the comment says the default should be the current token count so the limit cannot invalidate an existing initial marking. Places already holding more than one token become illegal at build time until the user raises the cap manually.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 42cf06a. Configure here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread libs/@hashintel/petrinaut-core/src/simulation/engine/capacity.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

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 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

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 e16c503. Configure here.

Comment thread libs/@hashintel/petrinaut-core/src/simulation/engine/capacity.ts
kube added 2 commits August 22, 2026 02:53
A place can declare a maximum token count, which participates in
transition enablement the standard way: a transition cannot fire if it
would take an output place above capacity. Output tokens apply at the
end of a frame, so the check folds in what earlier transitions this
frame already committed.

Deadlock detection uses the same check, so a net blocked only by full
output places is reported as deadlocked rather than stepping to
maxTime doing nothing. Nets without capacities keep empty constraint
tables and skip the check.
Replaces em dashes with full stops. No content change.
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