Skip to content

FE-1516: Accept bare function bodies for kernel, lambda, and dynamics code - #9370

Merged
kube merged 4 commits into
mainfrom
claude/fe-1516-bare-body-user-code
Aug 28, 2026
Merged

FE-1516: Accept bare function bodies for kernel, lambda, and dynamics code#9370
kube merged 4 commits into
mainfrom
claude/fe-1516-bare-body-user-code

Conversation

@kube

@kube kube commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

🌟 What is the purpose of this PR?

Transition kernels, lambdas, and differential equations can be written as a plain function body ending in return, the way metrics and scenario code already are. The export default TransitionKernel(...) / Lambda(...) / Dynamics(...) module form still works: both forms pass the LSP and lower to the same HIR. Visualizers are unchanged.

// A transition kernel, in full:
return { Target: [{ x: input.Source[0].x + parameters.step }] };
Surface Variables in scope Must return
Transition kernel input, parameters tokens per typed output place, keyed by place name
Lambda (predicate / stochastic) input, parameters boolean / firing rate
Differential equation tokens, parameters one derivative object per token
Metric (already bare-body) state, parameters a finite number
Scenario code (already bare-body) parameters, scenario, range initial tokens keyed by place name

🔍 What does this change?

  • hir/user-code-form.ts (new): classifies code as module or bare body. Any top-level export statement means module form. The HIR lowering and the LSP wrapping both call it, so the editor and the compiler always agree on a given source text.
  • hir/lower-typescript.ts: bare bodies lower through the wrapped-body path metrics already use. The code is wrapped in (input, parameters) => { ... } for parsing, spans are shifted back onto the raw text, and the lowered function carries the ambient names as its parameters. The type checker, analyses, and buffer emitters are unchanged.
  • LSP (generate-virtual-files.ts, create-language-service-host.ts): each kernel/lambda/dynamics virtual file carries a wrapper per form (VirtualFile.formWrappers), re-picked on every content write, per-keystroke updates included. The body wrapper types the code as a function body with typed input / tokens / parameters parameters and appends an unreachable return undefined as never; to its suffix: an unfinished body is not a TypeScript error (the HIR lint reports "must end with a return statement" at the right position), an empty lambda stays valid (the runtime default applies), and type mismatches keep exact messages ("not assignable to type 'boolean'").
  • Migrates the default templates, the six built-in examples, the Storybook nets, the AI assistant cheatsheet (ai.ts), and the entity-schema descriptions to the bare form.
  • User guide: petri-net-extensions.md gains a "Code surfaces and their variables" section with the table above and now teaches the bare form; useful-patterns.md snippets migrated. The arch page content/simulation/user-code.mdx documents the form detection.
  • Behaviour change: code without any export is read as a bare body, so const x = 1; now fails with "The function body must end with a return statement" instead of "Expected export default <Ctor>(...)".

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

⚠️ Known issues

  • The user-guide screenshots still show export default code in the editors and need re-capturing.
  • petrinaut-cli test fixtures keep the module form as backwards-compatibility coverage; brunch-agent inbox documents keep it too and still load.

🐾 Next steps

  • Re-capture the user-guide screenshots.

🛡 What tests cover this?

  • hir/user-code-form.test.ts:
    Form-detection table test
  • hir/lower-typescript.test.ts:
    Bare-body lowering for all three surfaces, module/body equivalence, span mapping, missing-return errors
  • lsp/lib/checker.test.ts:
    Both forms through the full checker, diagnostic positions, empty lambda, missing return reported by the HIR lint
  • lsp/lib/create-sdcpn-language-service.test.ts:
    Completions in both forms and re-wrapping when an edit switches form
  • examples/examples.test.ts:
    Every built-in example compiles through the HIR pipeline and passes the full LSP check

❓ How to test this?

  1. Open a net with a typed transition (e.g. the "Production with machine failure" example).
  2. In the Transition Results editor, write return { <Place>: [...] }; with no wrapper: completions offer input and parameters, and diagnostics point into the body.
  3. Paste an old export default TransitionKernel(...) module: it still type-checks.
  4. Run a simulation: both forms execute.

… code

Transition kernels, lambdas, and differential equations accept a bare
function body ending in `return`, with the input object (tokensByPlace /
tokens) and `parameters` ambient — like metrics and scenario code. The
legacy `export default <Ctor>(...)` module form is still accepted.

- HIR: detect the source form (any top-level export marks the module
  form) and lower bare bodies through the wrapped-body path with
  synthesized ambient parameters.
- LSP: dual-form virtual files pick the module or body wrapper from the
  current content on every write, so both forms type-check with
  completions; the body wrapper widens the return type with `| void`
  so unfinished bodies get the HIR's friendlier missing-return error and
  empty lambdas stay valid.
- Default templates, built-in examples, Storybook nets, the AI assistant
  guidance, and the user docs now use the bare form; visualizers are
  unchanged. New test compiles and LSP-checks every built-in example.
@kube kube self-assigned this Aug 27, 2026
@vercel

vercel Bot commented Aug 27, 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 27, 2026 4:35pm
hashdotdesign-tokens Ready Ready Preview Aug 27, 2026 4:35pm
petrinaut Ready Ready Preview Aug 27, 2026 4:35pm
petrinaut-docs Ready Ready Preview Aug 27, 2026 4:35pm

Request Review

@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 Aug 27, 2026
- Arch docs: user-code.mdx now describes the bare-body form as primary.
- LSP body wrapper: exact return type plus an unreachable
  `return undefined as never;` in the suffix instead of `| void`
  widening, so type-mismatch messages no longer mention a void the user
  never wrote while empty/unfinished bodies still defer to the HIR lint.
- Examples: replace the codemod's `return ({});` with `return {};`.
- Default predicate template no longer suggests `return Infinity;`
  (a type error for predicate lambdas).
- Direct table test for detectUserCodeForm; exact parse-error span
  assertion; checker test pinning where redeclared-ambient errors land.
- getFileContent includes the suffix, matching what TypeScript checks.
Kernels and lambdas read their tokens as `input` in the bare-body form
(dynamics keeps `tokens`). Templates, examples, AI guidance, schema
descriptions, and the user guide follow, and the guide gains a
"Code surfaces and their variables" table listing what is in scope per
surface.
@cursor

cursor Bot commented Aug 27, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes the user-code compilation and editor checking path for core simulation surfaces, but legacy module form remains supported and coverage is broad across HIR, LSP, and examples.

Overview
Transition kernels, lambdas, and differential equations can now be written as a plain function body ending in return, with input (or tokens for dynamics) and parameters in scope—matching metrics and scenario code. The legacy export default Lambda / TransitionKernel / Dynamics module form still compiles and type-checks.

A shared form detector (detectUserCodeForm) classifies each snippet as module vs body; the HIR lowerer wraps bare bodies for parsing and shifts diagnostics back onto user text, and the LSP applies per-form virtual-file wrappers so completions and types follow the active style (including when the user switches forms mid-edit).

Defaults, built-in examples, Storybook nets, AI assistant guidance, entity schema descriptions, and user docs are updated to the bare style. New tests compile all shipped examples through HIR and the full LSP check; existing tests expect bare default kernel templates.

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

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.50%. Comparing base (57396c9) to head (9aedf64).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9370      +/-   ##
==========================================
- Coverage   60.73%   60.50%   -0.24%     
==========================================
  Files        1440     1434       -6     
  Lines      143477   141340    -2137     
  Branches     6662     6612      -50     
==========================================
- Hits        87145    85519    -1626     
+ Misses      55240    54746     -494     
+ Partials     1092     1075      -17     
Flag Coverage Δ
apps.hash-api 14.68% <ø> (ø)
rust.hash-config ?
rust.hash-middleware ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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.

@kube
kube enabled auto-merge August 27, 2026 16:51
@kube
kube added this pull request to the merge queue Aug 28, 2026
Merged via the queue into main with commit 211a1a6 Aug 28, 2026
89 of 90 checks passed
@kube
kube deleted the claude/fe-1516-bare-body-user-code branch August 28, 2026 10:43
@hash-release hash-release Bot mentioned this pull request Aug 28, 2026
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 > backend Owned by the @backend team type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

3 participants