Skip to content

FE-1121: Add UUID token dimension type#8953

Open
kube wants to merge 3 commits into
cf/fe-1139-simulation-architecture-docsfrom
cf/fe-1121-add-uuid-discrete-type-to-petrinaut
Open

FE-1121: Add UUID token dimension type#8953
kube wants to merge 3 commits into
cf/fe-1139-simulation-architecture-docsfrom
cf/fe-1121-add-uuid-discrete-type-to-petrinaut

Conversation

@kube

@kube kube commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Adds uuid as a fourth colour element type: 128-bit RFC-4122 identifiers on tokens, enabling entity-identity modelling (track a specific token across places, correlate firings, derive stable IDs from data). This is the first token attribute wider than one f64 slot — it exercises the format-v2 packed-struct layout exactly as designed.

🔗 Related links

🚫 Blocked by

🔍 What does this change?

Representation (three layers):

  • Buffers: u64x2 physical kind — two 64-bit little-endian lanes (16 B, align 8; JS has no 128-bit load, so 8-byte alignment is deliberate), read/written through a shared BigUint64Array view. Whole-token byte copies keep arbitrary bit patterns intact — a NaN-payload UUID (ffffffff-ffff-4fff-bfff-…) round-trips through marking → dynamics → firing → compaction → clone → reader (tested).
  • Runtime: one 128-bit bigint — value-semantic (tokenA.id === tokenB.id just works), measured cheaper than the string form.
  • At rest (documents, scenario JSON): canonical lowercase 36-char strings, keeping everything JSON.stringify-safe.

Kernel semantics:

  • uuid output fields are optional: omitted ⇒ auto-generated from the seeded simulation RNG (deterministic per seed; v4 version/variant bits forced; never crypto.randomUUID).
  • Uuid.generate() (explicit seeded generation) and Uuid.from(value) (derive a stable ID from any value) are injected into user code as engine-resolved sentinels, mirroring Distribution. Forwarding an input token's uuid is plain pass-through.
  • Kernel token encoding is deduplicated into engine/encode-kernel-token.ts, shared by the interactive engine and Monte Carlo (was copy-pasted).
  • Distribution on a uuid field stays a type error (LSP) and a runtime error.

Coercion — total toUuid(value): bigint, never throws: valid UUID strings parse (any case → canonical); bigint in range passes through; anything else (numbers, free text) converts deterministically via UUIDv5 under a fixed exported PETRINAUT_UUID_NAMESPACE; nil UUID (0n) is the typed default.

LSP: input tokens type uuid as bigint; kernel outputs now generate per-element Output_<colour> types in both stochasticity modes with uuid?: bigint | string | PetrinautUuid; Uuid/PetrinautUuid declarations always available; dynamics derivatives keep uuid?: never.

UI: UUID option in the dimension type select; spreadsheet uuid cells show an 8-hex short form (full canonical string via title and when selected/edited; committed text goes through toUuid, so free text like alice yields a stable ID; Delete → nil); scenario drawers round-trip strings⇄bigints; the token-encoding playground renders u64x2 slots with lo/hi lane labels and the canonical string in the hover panel.

Docs/schema/AI guidance updated (petri-net-extensions.md + scenario/simulation notes, Zod descriptions, ai.ts).

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
    • ⚠️ the token-type screenshot in petri-net-extensions.md shows a 3-option dropdown and should be re-captured with UUID visible

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

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • The ticket's original "support any UUID smaller than 32 bytes" is deliberately narrowed to RFC-4122 128-bit only — display, v5 coercion, and the uuid library all assume 16 bytes; the layout machinery generalizes to u64×4 later if a real need for wider IDs appears (noted on the ticket).
  • Metrics receive uuid values as bigint — fine for comparison/counting, but JSON.stringify on a whole token would throw in user code (standard bigint behaviour).

🐾 Next steps

  • Re-capture the dimension-type screenshots (3 → 4 options).
  • Architecture docs engine page: flip the uuid row in the format-v2 table from "future" to shipped (can ride this PR or a follow-up).

🛡 What tests cover this?

  • New engine/uuid.test.ts (parse/format, toUuid fixtures incl. stable v5, generateUuidFromRng determinism + version/variant bits)
  • token-layout u64x2 sizing/offset/lane tests; end-to-end NaN-payload pipeline test in compute-next-frame.test.ts
  • Kernel tests: omitted-uuid seeded determinism, Uuid.from stability, forwarding, Distribution-on-uuid throws
  • LSP checker tests: bigint input typing, optional/string/Uuid.generate() outputs valid, Distribution on uuid invalid
  • compile-scenario canonical-string normalization; spreadsheet/playground unit tests
  • Totals: 592 core + 139 UI tests, lint:tsc + lint:eslint clean

❓ How to test this?

  1. Checkout the branch, yarn dev in libs/@hashintel/petrinaut.
  2. Add a UUID dimension to a type; open a place's initial state — cells show short-form IDs; type free text into a cell and watch it become a stable UUID.
  3. Author a kernel omitting the uuid field → run twice with the same seed → identical generated IDs; use Uuid.from(input.Source[0].some_value) for derived IDs.
  4. Open Dev / Token Encoding Playground, add a UUID dimension: the memory view shows the 16-byte u64x2 slot with lo/hi lanes and the canonical string on hover.

📹 Demo

The playground story renders the exact wire format: a UUID slot with lane labels and the input → stored → reads-back round-trip.

128-bit RFC-4122 UUIDs as a fourth colour element type:

- Buffers: two 64-bit little-endian lanes (u64x2, 16 B, align 8) read
  through a shared BigUint64Array view; whole-token byte copies keep
  arbitrary bit patterns intact (no NaN-canonicalization hazard).
- Runtime: one 128-bit `bigint` (value-semantic ===). At rest
  (documents, scenario JSON): canonical lowercase strings.
- Kernels: uuid output fields are optional — omitted values are
  auto-generated from the seeded simulation RNG (deterministic per
  seed, v4 version/variant bits). Explicit `Uuid.generate()` and
  `Uuid.from(value)` sentinels are injected into user code like
  `Distribution`; forwarding an input uuid is plain pass-through.
  Kernel encoding is deduplicated into engine/encode-kernel-token.ts,
  shared by the interactive and Monte Carlo paths.
- Coercion: total `toUuid` — valid strings parse, anything else
  (numbers, free text) converts deterministically via UUIDv5 under a
  fixed Petrinaut namespace; nil UUID (0n) is the typed default.
- LSP: inputs type uuid as `bigint`; kernel outputs generate
  per-element Output_ types in both stochasticity modes with
  `uuid?: bigint | string | PetrinautUuid`; Distribution stays
  rejected for uuid; dynamics derivatives stay `?: never`.
- UI: UUID option in the dimension type select; spreadsheet uuid cells
  show an 8-hex short form (full string on select/edit, free text
  v5-converted on commit); scenario forms round-trip strings⇄bigints;
  playground renders u64x2 slots with lo/hi lane labels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 3, 2026 16:59
@vercel

vercel Bot commented Jul 3, 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, Comment Jul 4, 2026 1:05am
petrinaut Ready Ready Preview, Comment Jul 4, 2026 1:05am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Jul 4, 2026 1:05am

@cursor

cursor Bot commented Jul 3, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches core simulation frame layout, transition firing, and RNG consumption paths; mistakes could corrupt token bytes or break determinism, though coverage includes NaN-payload round-trips and kernel RNG tests.

Overview
Introduces uuid as a fourth colour element type so tokens can carry stable 128-bit identities across places and firings.

Engine & storage: Frame buffers gain a u64×2 physical layout (two little-endian lanes via BigUint64Array, exposed as bigint in lambdas/kernels). At-rest data (documents, scenarios) uses canonical lowercase UUID strings. Coercion is total via toUuid (parse valid strings; otherwise deterministic UUIDv5 under a fixed namespace). Kernel encoding is centralized in encode-kernel-output-token, shared by the step engine and Monte Carlo.

Kernel semantics: uuid outputs may be omitted or set with Uuid.generate() / Uuid.from(value) (runtime sentinels, always injected like Distribution is optional). Omitted/generate draws seeded v4-shaped IDs; non-UUID literals coerce via v5. Distribution on uuid remains invalid.

Authoring surface: Schema, LSP virtual types (bigint inputs; optional kernel outputs), AI cheatsheet, default kernel templates, scenario compile (string rows), and initial-marking APIs are extended. UI: type picker + spreadsheet uuid columns (short display, full edit, scenario drawer string⇄bigint round-trip) and token-encoding playground lo/hi lane view.

Reviewed by Cursor Bugbot for commit c903c36. 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 labels Jul 3, 2026

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.

Pull request overview

Adds a new uuid token dimension type to Petrinaut (UI + persistence + simulation/LSP), represented as bigint at runtime, encoded as u64x2 in frame buffers, and stored as canonical UUID strings at rest.

Changes:

  • Introduces UUID parsing/formatting/coercion (toUuid, seeded generation, v5 fallback) and wires it through kernel output encoding and token-layout packed structs.
  • Extends UI editors (type dropdown, initial state + scenario spreadsheets, encoding playground) to display/edit UUIDs and round-trip at-rest strings ⇄ runtime bigints.
  • Updates schemas, LSP virtual files, AI guidance, and adds broad test coverage for UUID behavior and determinism.

Reviewed changes

Copilot reviewed 48 out of 48 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
libs/@hashintel/petrinaut/src/ui/views/shared/place-state-visualization.tsx Coerces initial marking token records so uuid strings become runtime bigints.
libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/scenarios/view-scenario-drawer.tsx Plumbs token-row context and uses mapping helpers for scenario spreadsheet data.
libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/scenarios/scenario-mapping.ts Converts scenario persisted rows (uuid strings) ⇄ spreadsheet runtime values (bigints).
libs/@hashintel/petrinaut/src/ui/views/Editor/panels/SimulateView/scenarios/create-scenario-drawer.tsx Provides token-row context when creating new scenarios.
libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/type-properties/subviews/main.tsx Adds UUID to the dimension type selector.
libs/@hashintel/petrinaut/src/ui/views/Editor/panels/PropertiesPanel/place-properties/subviews/place-initial-state/initial-state-editor.tsx Parses at-rest uuid strings to bigints for the initial marking spreadsheet.
libs/@hashintel/petrinaut/src/ui/lib/compile-visualizer.ts Allows visualizers to receive bigint token attributes.
libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/token-memory-view.tsx Displays uuid values in canonical string form and labels u64x2 lanes.
libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/playground-monaco.ts Types uuid fields as `bigint
libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/physical-layout.ts Updates token decode to use unified token views; documents u64x2 bit ordering.
libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/physical-layout.test.ts Adds layout/round-trip/bit-order coverage for uuid (u64x2) fields.
libs/@hashintel/petrinaut/src/ui/dev/token-encoding-playground/dimension-editor.tsx Adds UUID option in the playground dimension editor.
libs/@hashintel/petrinaut/src/ui/components/spreadsheet.tsx Supports uuid columns (bigint values), display truncation, and text editing/coercion.
libs/@hashintel/petrinaut/docs/simulation.md Documents UUID behavior in the initial marking spreadsheet.
libs/@hashintel/petrinaut/docs/scenarios.md Documents UUID behavior in scenario spreadsheets.
libs/@hashintel/petrinaut/docs/petri-net-extensions.md Documents the new UUID dimension type and kernel semantics.
libs/@hashintel/petrinaut-core/src/types/sdcpn.ts Extends element types + token attribute value types; updates scenario row typing.
libs/@hashintel/petrinaut-core/src/simulation/monte-carlo/transition-effect.ts Reuses shared kernel-token encoder and unified token views.
libs/@hashintel/petrinaut-core/src/simulation/monte-carlo/frame-reader.ts Reads tokens via unified token views.
libs/@hashintel/petrinaut-core/src/simulation/monte-carlo/frame-buffer.ts Adds shared f64/u64/u8 token region views to Monte Carlo frames.
libs/@hashintel/petrinaut-core/src/simulation/frames/internal-frame.ts Adds shared f64/u64/u8 token region views to engine frames.
libs/@hashintel/petrinaut-core/src/simulation/frames/frame-reader.ts Reads tokens via unified token views.
libs/@hashintel/petrinaut-core/src/simulation/engine/uuid.ts Implements uuid parsing/formatting/total coercion + seeded v4 generation.
libs/@hashintel/petrinaut-core/src/simulation/engine/uuid.test.ts Tests uuid parsing/formatting/coercion and RNG-based generation invariants.
libs/@hashintel/petrinaut-core/src/simulation/engine/types.ts Expands kernel output typing to include optional uuid + uuid sentinels.
libs/@hashintel/petrinaut-core/src/simulation/engine/token-values.ts Adds uuid default/coercion and updates encoder return type for uuid lanes.
libs/@hashintel/petrinaut-core/src/simulation/engine/token-layout.ts Adds u64x2 physical kind + BigUint64Array view; reads/writes uuid lanes.
libs/@hashintel/petrinaut-core/src/simulation/engine/token-layout.test.ts Adds layout + lane-level correctness tests for uuid fields.
libs/@hashintel/petrinaut-core/src/simulation/engine/token-layout.test-helpers.ts Updates helpers to use unified token views.
libs/@hashintel/petrinaut-core/src/simulation/engine/encode-kernel-token.ts Deduplicates kernel output token encoding (Distribution + uuid sentinel handling).
libs/@hashintel/petrinaut-core/src/simulation/engine/compute-possible-transition.ts Uses shared kernel-token encoder and unified token views.
libs/@hashintel/petrinaut-core/src/simulation/engine/compute-possible-transition.test.ts Adds behavioral tests for uuid kernel outputs (omit/generate/from/forward/errors).
libs/@hashintel/petrinaut-core/src/simulation/engine/compute-next-frame.test.ts Adds end-to-end test ensuring uuid lanes survive pipeline without NaN canonicalization.
libs/@hashintel/petrinaut-core/src/simulation/engine/build-simulation.ts Reads tokens via unified token views for dynamics input.
libs/@hashintel/petrinaut-core/src/simulation/authoring/user-code/uuid-runtime.ts Adds Uuid.generate/from sentinels for user code.
libs/@hashintel/petrinaut-core/src/simulation/authoring/user-code/compile-user-code.ts Injects Uuid runtime code unconditionally into compiled user code.
libs/@hashintel/petrinaut-core/src/simulation/authoring/user-code/compile-user-code.test.ts Tests Uuid runtime injection (including when Distribution is disabled).
libs/@hashintel/petrinaut-core/src/simulation/authoring/scenario/compile-scenario.ts Normalizes uuid values to canonical strings for JSON-safe compiled scenario output.
libs/@hashintel/petrinaut-core/src/simulation/authoring/scenario/compile-scenario.test.ts Adds scenario compilation tests for uuid normalization and deterministic v5 mapping.
libs/@hashintel/petrinaut-core/src/simulation/api.ts Introduces JSON-safe initial marking typing that can accept uuid strings.
libs/@hashintel/petrinaut-core/src/schemas/scenario-schema.ts Allows strings in persisted token rows (to support uuid-at-rest strings).
libs/@hashintel/petrinaut-core/src/schemas/entity-schemas.ts Extends element type schema with uuid and updates descriptions.
libs/@hashintel/petrinaut-core/src/lsp/lib/generate-virtual-files.ts Types uuid as bigint; generates kernel output types with optional uuid + sentinels.
libs/@hashintel/petrinaut-core/src/lsp/lib/checker.test.ts Adds LSP checker tests around uuid typing and invalid Distribution-on-uuid outputs.
libs/@hashintel/petrinaut-core/src/index.ts Exports uuid utilities and token region views.
libs/@hashintel/petrinaut-core/src/default-codes.ts Uses Uuid.generate() as the default value source for uuid elements.
libs/@hashintel/petrinaut-core/src/ai.ts Updates AI guidance for uuid typing and kernel/scenario semantics.
.changeset/fe-1121-uuid-token-dimension-type.md Publishes a minor changeset describing the uuid dimension feature.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +47 to +51
if (typeof cell === "string") {
return elements?.[columnIndex]?.type === "boolean"
? cell.trim().toLowerCase() === "true"
: Number.parseFloat(cell) || 0;
}
Comment on lines +29 to +33
if (column.type === "uuid") {
return toUuid(raw);
}
return typeof raw === "string" ? Number.parseFloat(raw) || 0 : raw;
};
- Selecting a uuid spreadsheet cell now expands it to the full
  canonical string in an opaque overlay that spills over neighbouring
  cells (anchored right for columns in the right half of the table so
  the scroll container never clips it); unselected cells keep the
  short 8-hex form with the full value as a hover tooltip. New
  "Typed columns" spreadsheet story covers all four dimension types.
- generateUuidFromRng now takes the top 16 bits of eight draws instead
  of 32 bits of four: the seeded LCG's multiply exceeds 2^53 for large
  states, so each draw's low ~8 bits are float-precision artifacts —
  previously every generated UUID visibly ended each word in zeros.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 4, 2026 00:58

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.

Pull request overview

Copilot reviewed 50 out of 50 changed files in this pull request and generated 3 comments.

Comment on lines +52 to +55
export function formatUuid(value: bigint): string {
const hex = value.toString(16).padStart(32, "0");
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}
Comment on lines +241 to +242
const toCanonicalUuidString = (value: SpreadsheetCellValue): string =>
formatUuid(typeof value === "bigint" ? value : toUuid(value));
Comment on lines +67 to +72
if (
elements?.[columnIndex]?.type === "uuid" ||
typeof cell === "bigint"
) {
return formatUuid(typeof cell === "bigint" ? cell : toUuid(cell));
}
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.

2 participants