Skip to content

FE-1500: Add example models and the read-only example page - #9362

Merged
kube merged 3 commits into
mainfrom
codex/fe-1500-examples
Sep 1, 2026
Merged

FE-1500: Add example models and the read-only example page#9362
kube merged 3 commits into
mainfrom
codex/fe-1500-examples

Conversation

@kube

@kube kube commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Publishes seven Petrinaut models as website examples: canonical /examples/<slug> pages rendering the full Petrinaut component read-only, with the scenario, subnet, and canvas selection encoded in the URL. Examples are embed and deep-link content, so there is no index page: /examples redirects to /. Stacked on the controlled-navigation PR.

🔗 Related links

🔍 What does this change?

  • Adds the model sources under src/examples/models. The precompiled HIR runtime artifacts are NOT committed: scripts/generate-example-artifacts.ts writes them to the gitignored src/examples/generated/ at build time. A new examples:generate Turbo task feeds build, lint:tsc, and test:unit, so local builds, CI, and Vercel deploys (which build through Turbo) all generate them; yarn dev generates before starting Vite.
  • Adds the catalog: metadata (titles and safe parameter bounds for scenario sliders), a cached model loader, and a runtime-artifact loader. The metadata module stays free of Petrinaut runtime imports so the oEmbed Vercel function can validate URLs without bundling models.
  • Adds the canonical /examples/$slug route rendering <Petrinaut readonly> from an in-memory read-only document handle, and an /examples redirect to /. (The review presentation profile that hides — rather than disables — authoring controls arrives later in the stack, in FE-1500: add editor presentation profiles #9425.)
  • Encodes the URL contract in example-search.ts: scenario, subnet, and canvas selection. The module is deliberately free of React and of the editor, so the canonical page, the embed page (FE-1500: Add the example embed route #9427), and the oEmbed function (FE-1500: Add oEmbed discovery and framing security #9363) all speak one contract instead of re-deriving it. Validation doubles as normalization, equality is defined as "the canonical query strings agree", and the selection vocabulary comes from @hashintel/petrinaut-core/selection.
  • Per-key search validation runs through zod (already a dependency): string fallbacks are .optional().catch(undefined) schemas, and the "type:id" selection encoding is a bidirectional z.codec.
  • navigation-search.ts keeps only the projection onto Petrinaut's navigation state. Everything the URL does not carry (mode, Simulate section, drawers) lives in page state through the useSharedSearchNavigation hook, which keeps the full location in memory and mirrors only its shared projection to the URL, so controls driving non-shared fields never silently snap back.
  • The codec laws are property-tested with fast-check: decoding never throws on generated garbage, encode(decode(s)) is a fixed point (what the embed redirect relies on), and every decodable state survives encode then decode.
  • Review fixes: a rejected example-chunk import evicts the loader cache so a transient failure or stale deploy is retryable without a hard reload; the URL codec round-trips all four editor modes (notebook and actual no longer snap back to edit); the routed demo prunes abandoned empty nets again (a Back entry to a pruned net falls back to the most recent one); the generation script iterates the canonical exampleSlugs list instead of its own copy.
  • Review fixes (second round): restated env: ["TEST_COVERAGE"] on the test:unit override, which replaces rather than merges with the root task; sharedSearchKeys is module-private, as nothing imported it; hoisted useSearch out of a JSX prop position; added a test asserting the truck-fleet rewrite initialises every attribute of the Truck and Conditions types, so a field added to either fails here instead of simulating with a silent default; dropped the /examples* rewrites, now covered by the SPA fallback in FE-1500: Add Petrinaut website routing #9359.

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • does not modify any publishable blocks or libraries, or modifications do not need publishing

📜 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:

  • affected the execution graph, and the turbo.json's have been updated to reflect this

🛡 What tests cover this?

  • New: catalog.test.ts (every catalog entry loads, every slug has a catalog entry, every scenario has generated HIR, one cached load per example), normalize-example.test.ts (the truck-fleet rewrite fires on the real model; other slugs pass through), example-search.test.ts, example-search.property.test.ts (fast-check contract laws over generated search inputs: decoding never throws, validation is idempotent, selection survives encode then decode, the canonical string re-decodes to the same location), navigation-search.test.ts (state projection), use-shared-search-navigation.test.tsx (in-memory fields survive URL merges; only shared changes write the URL), readonly-example-handle.test.ts.
  • CI: generation runs before every build, typecheck, and test task via the Turbo graph, and fails the task when a model or scenario no longer compiles.

❓ How to test this?

  1. Checkout the branch and run yarn workspace @apps/petrinaut-website dev.
  2. Open /examples/gases-1-pn, select items, switch subnets, and pick a scenario.
  3. Confirm the URL tracks those steps and Back/Forward walk the same states, that mode switching works without touching the URL, and that editing is not possible.

@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
hash Ready Ready Preview Sep 1, 2026 8:34am UTC
petrinaut Ready Ready Preview Sep 1, 2026 8:34am UTC
petrinaut-docs Ready Ready Preview Sep 1, 2026 8:34am UTC
1 Skipped Deployment
Project Deployment Actions Updated
hashdotdesign-tokens Ignored Ignored Preview Sep 1, 2026 8:34am UTC

Request Review

@vercel
vercel Bot temporarily deployed to Preview – petrinaut-docs August 26, 2026 23:24 Inactive
@cursor

cursor Bot commented Aug 26, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New user-facing routes and a build step that must stay aligned with simulation artifacts; mistakes surface as broken examples or failed CI rather than security issues.

Overview
Adds seven published Petrinaut models as deep-linkable website examples at /examples/<slug>, each rendered as a read-only full-screen Petrinaut instance with no example index (/examples redirects home).

Catalog and runtime loading split lightweight metadata (catalog-metadata.ts, no core runtime) from model + precompiled artifact loaders (catalog.ts), with cached loads and cache eviction on failed imports. Scenario slider bounds live in the catalog and are validated in tests against each model’s scenarios.

Build-time compilation: scripts/generate-example-artifacts.ts compiles every slug’s SDCPN to HIR and per-scenario runtime JSON into gitignored src/examples/generated/. yarn dev and Turbo wire examples:generate ahead of build, typecheck, and tests so CI/deploy fails when a model no longer compiles.

Shareable URL contract (example-search.ts): scenario, subnet, and at most one focused canvas item, validated/normalized via zod and covered by unit + fast-check property tests. The full example page syncs only that subset to the URL while other editor navigation stays in memory.

Model JSON lives under src/examples/models/ (including truck-fleet normalization for published examples where applicable).

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

Comment thread apps/petrinaut-website/src/examples/catalog.ts
Comment thread apps/petrinaut-website/src/routes/examples.$slug.index.tsx Outdated
Comment thread apps/petrinaut-website/src/routes/index.tsx Outdated
Comment thread apps/petrinaut-website/src/routes/index.tsx Outdated
Comment thread apps/petrinaut-website/src/main/app/local-storage-demo/local-storage-demo-app.tsx Outdated
Comment thread apps/petrinaut-website/turbo.json
Comment thread apps/petrinaut-website/turbo.json Outdated
Comment thread apps/petrinaut-website/turbo.json Outdated
Comment thread apps/petrinaut-website/src/examples/full-example-page.tsx
@semgrep-code-hashintel

Copy link
Copy Markdown

Semgrep found 1 regex_dos finding:

  • apps/petrinaut-website/src/main/app/brunch-demo/brunch-endpoint.ts

Ensure that the regex used to compare with user supplied input is safe from regular expression denial of service.

Comment thread apps/petrinaut-website/.tanstack/tmp/507d46af-61db48957dccd63d09a772feda53370f Outdated

@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.

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 6d45bac. Configure here.

Comment thread apps/petrinaut-website/src/examples/use-shared-search-navigation.ts
CiaranMn
CiaranMn previously approved these changes Aug 31, 2026
CiaranMn
CiaranMn previously approved these changes Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps area/deps Relates to third-party dependencies (area) area/infra Relates to version control, CI, CD or IaC (area)

Development

Successfully merging this pull request may close these issues.

3 participants