Run a library model in the reader's browser - #515
Open
GondekNP wants to merge 1 commit into
Open
Conversation
A page whose model can execute in a browser gets a "Run it here" button. The engine is the same one the command line uses, compiled to WebAssembly and staged into the landing site by landing/war/get_from_jar.sh -- the same arrangement the editor and the demo already have, since each is deployed to its own host. The run box reads the model out of the listing already on the page rather than holding a copy of it, so the code that runs is necessarily the code the reader is looking at. Nothing here transcribes Josh and nothing can drift from the authored file. The variables offered in the result plot are discovered from the result -- the records know their own attribute names -- so a page gains a new plot by the model exporting a new value, with no front-matter change. browserRunnable is derived rather than declared: the harvest asks the jar whether every target a simulation declares uses the memory protocol. WebAssembly has no filesystem, so a file:// target aborts the run, and a model declaring no target at all qualifies -- it still executes, which is the point for one whose assert handlers are the result. That gives five pages today: the three guides, plus hello_debug and spinup. spinup_export is correctly excluded for its file:// target. Data publishing moves from deployStatic into `joshdocs render`, which decompresses the committed .jshdz beside the page that declares it. One rule in one language, and a local preview now serves the same bytes from the same paths as the deployed site -- which is what lets the run button work before anything has been deployed. Guide prose links data by its published name, so those links are checked by the dead-link pass for the first time; site-absolute ones never were. The browser JavaScript is copied byte-for-byte from demo.joshsim.org/js/, with runner.js the only new file. Nothing in CI lints or executes JavaScript, so a proven copy beats new code. Verified by executing the engine headlessly rather than reading it: hello (34,782 records), hello_debug (+347,820 debug messages), grass_shrub_fire (91,728 records read from the published .jshd) and spinup (10 steps, no exports) all run, a sabotaged assert surfaces as "Assertion failed for assert.alwaysFails", and each page's listing unescapes to the authored .josh byte for byte. Note the engine currently runs as JavaScript rather than WebAssembly: the WasmGC build has failed validation since #334, tracked in #514. The run box works either way. 178 tests, ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2 of the runnable-docs work: a library page whose model can execute in a browser gets a Run it here button.
What a reader gets
Five pages today — the three guides, plus
hello_debugandspinup. The button runs the model on the same engine the CLI uses, then plots the result with the demo's existing scrub timeline and grid heatmap.The part that matters
The run box reads the model out of the listing already on the page. It does not carry its own copy, so the code that runs is necessarily the code the reader is looking at. There is nothing to keep in sync because there is no second copy — the same promise the rest of this pipeline makes, extended to execution.
The plotted variables are discovered from the result, not declared: the records know their own attribute names. A page gains a new plot by the model exporting a new value, with no front-matter change.
browserRunnable is derived, not declared
The harvest asks the jar (
inspect-exports) whether every target a simulation declares uses thememoryprotocol. WebAssembly has no filesystem, so afile://target aborts the run. A model declaring no target at all qualifies — it still executes, which is the whole point for one whoseasserthandlers are the result, and that is howspinupearns a button.spinup_exportis correctly excluded for itsfile://target. A unit naming nosimulationis excluded rather than guessed at, and a model the engine rejected is never offered.Cost is 10 extra JVM starts, gated by the same sound text check
inspect-externalsalready uses: harvest goes 40s → 52s.Data publishing moves into the renderer
joshdocs rendernow decompresses the committed.jshdzbeside the page that declares it, atdata/<unit id>/<name>.jshd. This retires theOrganize tutorial data for guidesstep indeployStatic.One rule in one language, and a local preview serves the same bytes from the same paths as the deployed site — which is what lets the run button work before anything has been deployed. Nesting under the unit id matters: declared filenames are only unique within a directory, so a flat layout would quietly serve one guide's
precipitation.jshdto another.Guide prose now links data by its published name, so those links are checked by the dead-link pass for the first time — the site-absolute
/guides/...spellings never were.Verified by running it, not reading it
Nothing in CI lints or executes JavaScript, so the browser files under
landing/js/are copied byte-for-byte fromdemo.joshsim.org/js/;runner.jsis the only new one. To check it anyway I installed node locally (not on the build path) and drove the engine headlessly:hellohello_debuggrass_shrub_fire.jshdspinupPlus: a sabotaged
assertsurfaces asAssertion failed for assert.alwaysFails, which is the path the status line reports; every runnable page's listing unescapes to the authored.joshbyte for byte; the published data is byte-identical toxz -d; and every import specifier in the module graph resolves.Follow-ups
JoshJsSimFacade.runSimulation→wasm.worker.js→wasm.js, so a browser run is as deterministic as CI.landing/js/is now a third copy of six files thateditor/js/anddemo.joshsim.org/js/already share. Five are byte-identical across all three and the sixth (wasm.js) differs by five lines that are a strict improvement — a dedupe is cheap and safe, but it touches the editor and demo, which nothing tests.178 tests, ruff clean.
🤖 Generated with Claude Code