Skip to content

FE-1429: Run trial replicates through the shared experiment runtime - #9358

Merged
kube merged 15 commits into
mainfrom
cf/fe-1429-run-trial-replicates-through-the-shared-experiment-backend
Sep 3, 2026
Merged

FE-1429: Run trial replicates through the shared experiment runtime#9358
kube merged 15 commits into
mainfrom
cf/fe-1429-run-trial-replicates-through-the-shared-experiment-backend

Conversation

@kube

@kube kube commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Before this PR, CLI ran a trial's seeded replicates sequentially, as FE-1408 left it. Parallelism waited until the CLI and the editor could share one worker story.

A trial now runs as one Monte Carlo experiment, sharded across worker_threads, through the same runtime the editor uses. Replicate 0 keeps the base seed verbatim. Two identical trials return identical replicates, so the common-random-numbers contract holds end to end.

$ node dist/cli.js serve --optimization-stdin --stdio   # seedsPerTrial: 4
evaluate: objective=11042.38 replicates=[(1234, 11040.77), (1013905460, 11043.01), …]
evaluate: objective=11042.38 replicates=[(1234, 11040.77), (1013905460, 11043.01), …]

Links

Changes

CLI

  • optimization.evaluate in @hashintel/petrinaut-cli builds one experiment per trial

    runCount = seedsPerTrial, runs from deriveTrialSeeds with replicate 0 as the base seed verbatim, and never more shards than replicates.

  • New --threads <n> flag

    Default is os.availableParallelism() - 1.
    --threads 1 spawns no workers and simulates on the calling thread.

  • Oversubscription was measured and rejected

    On a 10-core machine the 2000-run SIR benchmark completes in 1036 ms at 9 shards, 1005 ms at 10, 1501 ms at 20, and 1551 ms at 40.
    Simulation workers never block, so threads beyond the core count only add context switching and drag the merge watermark.

  • Second Vite entry builds simulation-worker.js beside cli.js

    createNodeSimulationWorkerFactory spawns it per shard and adapts Node's .on("message", value) events to the structural WorkerLike.
    Running from source, with no bundle, falls back to the in-process worker with a one-line stderr notice.

  • PetrinautCompiledModel exposes its sanitized sdcpn and hirArtifacts

    Both were already computed. CLI hands the pair to the experiment without recompiling.

  • Replicates no longer fail fast on a non-finite objective

    Contract change.
    They run in parallel and validate together.
    Error and wire format are unchanged.

Core

  • Experiment runtime defaults to one shard

    Hosts state their own parallelism.
    Editor's provider passes getDefaultMonteCarloShardCount(), which is navigator.hardwareConcurrency - 1.

  • Worker body moves into attachMonteCarloWorker(runtime) in @hashintel/petrinaut-core

    Moved out of monte-carlo.worker.ts.
    A runtime is post, receive, and yield, so Web Workers, Node worker_threads, and an in-process callback loopback run the same protocol.
    Web entry is two lines and createInProcessMonteCarloWorker is the no-thread fallback.

  • Experiments accept explicit per-run configs as runs

    Length-checked against runCount and sliced per shard.
    A caller can pin every run's seed.

  • Experiment handle exposes per-run final metric values as a runResults store

    Each worker posts a runResults message before complete.
    Runs are disjoint across shards, so merging is a map union without the frame watermark.
    GPU handle exposes an empty store, since its metrics reduce on-device.

  • Worker-sharding architecture page gains a D2 diagram of the thread hosts

    Section "One protocol, any thread host" shows how each host provides, instantiates, and talks to its workers.

Test coverage

  • Core experiment.test.ts, four new tests:

    Shard slicing of explicit run configs, runs/runCount mismatch rejection, per-run results across in-process worker shards, and the local path's per-run values.

  • CLI optimization.test.ts, simulation-threads.test.ts, protocol.test.ts:

    Optimization suite reworked onto a createExperiment seam, so tests assert the exact config production sends: marking, parameter values, seed list.
    One end-to-end trial through in-process workers asserts seeds, finiteness, mean, and cross-trial determinism.

  • Built binary verified by hand:

    4-seed trial over real worker_threads returns identical replicates across two evaluates.

How to test

  • turbo run build --filter '@hashintel/petrinaut-cli'
  • Send optimization manifest with execution.seedsPerTrial: 4 to node dist/cli.js serve --optimization-stdin --stdio
  • Evaluate same parameters twice
  • Expect identical replicates across both evaluates and replicate 0 carrying execution.seed

@kube kube self-assigned this Aug 26, 2026
@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 2, 2026 7:51pm UTC
petrinaut Ready Ready Preview Sep 2, 2026 7:51pm UTC
petrinaut-docs Ready Ready Preview Sep 2, 2026 7:51pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
hashdotdesign-tokens Ignored Ignored Preview Sep 2, 2026 7:51pm UTC

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 type/eng > backend Owned by the @backend team labels Aug 26, 2026
@kube
kube force-pushed the cf/fe-1429-run-trial-replicates-through-the-shared-experiment-backend branch from 5b1a2c9 to 840d731 Compare August 27, 2026 16:40
@kube
kube force-pushed the cf/fe-1429-run-trial-replicates-through-the-shared-experiment-backend branch from 840d731 to 4ea66ab Compare August 27, 2026 18:24
@kube
kube force-pushed the cf/fe-1429-run-trial-replicates-through-the-shared-experiment-backend branch from 4ea66ab to 8bae884 Compare August 30, 2026 13:08
@kube
kube marked this pull request as ready for review August 31, 2026 09:25
@cursor

cursor Bot commented Aug 31, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes the optimization evaluation path and parallel worker lifecycle, but seeds and results are explicitly pinned and covered by new tests; the main behavioral shift is post-hoc replicate validation instead of fail-fast.

Overview
Optimization trials no longer call model.run once per seed in sequence. Each optimization.evaluate now runs one sharded Monte Carlo experiment with explicit per-replicate seeds, merged runResults, and the same mean/replicate wire shape.

Core pulls the worker loop into attachMonteCarloWorker(runtime) plus an in-process fallback, adds runs / runResults, and exposes sdcpn + hirArtifacts on compiled models. Experiments default to one shard; hosts choose parallelism (getDefaultMonteCarloShardCount in the editor, --threads in the CLI).

CLI adds --threads <n> (default cores − 1; 1 = no worker_threads), a simulation-worker.js bundle, and Node WorkerFactory wiring. Missing the worker bundle falls back to in-process with a stderr notice.

Replicates still use pinned seeds and common random numbers; sharding only affects wall time. Invalid objectives are validated after all replicates finish (no fail-fast on the second seed).

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

kube added 10 commits September 2, 2026 21:20
rolldown-plugin-dts writes experiments.d.ts and webgpu.d.ts. The .d.d.ts names came from the previous toolchain's output and resolve to nothing for a published consumer.
The worker body moves to attachMonteCarloWorker(runtime), so Web Workers,
Node worker_threads, and an in-process loopback all run the same protocol.
Experiments accept explicit per-run configs (sliced per shard), and each
run's final metric values come back as runResults — the per-seed objectives
optimization replicates need.
Each trial becomes one Monte Carlo experiment: explicit run seeds keep
replicate 0 on the base seed, worker_threads carry the shards under Node,
and the in-process worker covers source runs and tests. Replicates no
longer fail fast — they run in parallel and validate together.
The experiment stops sniffing navigator and defaults to one shard. The
editor's provider passes getDefaultMonteCarloShardCount(); the CLI gains
--threads, defaulting to one per core minus one, where --threads 1 spawns
no workers and simulates on the calling thread.

@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 f123fc0. Configure here.

Comment thread libs/@hashintel/petrinaut-cli/src/runtime/optimization.ts
YannisZa
YannisZa previously approved these changes Sep 3, 2026
@kube
kube added this pull request to the merge queue Sep 3, 2026
Base automatically changed from cf/fe-1340-webgpu-experiment-backend to main September 3, 2026 12:03
@kube
kube requested a review from a team as a code owner September 3, 2026 12:03
@github-actions github-actions Bot added area/deps Relates to third-party dependencies (area) area/apps > hash.design Affects the `hash.design` design site (app) labels Sep 3, 2026

@TimDiekmann TimDiekmann left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

infra ✅

@kube
kube dismissed YannisZa’s stale review September 3, 2026 12:04

The merge-base changed after approval.

Merged via the queue into main with commit 17b6d1c Sep 3, 2026
257 of 283 checks passed
@kube
kube deleted the cf/fe-1429-run-trial-replicates-through-the-shared-experiment-backend branch September 3, 2026 12:06
@hash-release hash-release Bot mentioned this pull request Sep 3, 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/deps Relates to third-party dependencies (area) 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.

4 participants