Commit the guides' tutorial data instead of fetching it at build time - #511
Merged
GondekNP merged 3 commits intoAug 4, 2026
Merged
Conversation
two_trees and grass_shrub_fire are the only library units that read an external, and neither declared a data file. The manifest recorded that the models reach precipitationTulare/temperatureTulare/precipitation but not which files provide them, so the rendered pages showed no Data row. That gap is worse than a missing label. .gitignore excludes *.jshd and CI preprocesses the tutorial data into an artifact, so the declaration is the only record in the repository that a run needs the file at all -- a reader who downloaded the model got "External resource not found" naming a file the page never mentioned. The harvest now fails when a runnable unit reads an external no declared file provides, pointing the author at the sidecar. Conformance tests are exempt: the test harness stages their fixtures, sometimes generating them, so the author of a test does not own the file. Verified by staging only the declared files -- both guides run to exit 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The two data-driven guides declared what they need but the files were not in the repository. They were preprocessed on every push from a zip fetched over the network -- landing/test_preprocess.sh wgets it from joshsim.org, the site the build publishes to -- so the input to the build was a mutable URL. A change in it would alter the published guides with no commit and no diff, and a 404 would fail four jobs. Compression is what makes committing viable. .jshdz is XZ-wrapped .jshd, and the three files are 3.5 MB expanded against 34 KB compressed. The engine reads them directly: MultiFormatExternalGetter resolves a bare external name to <name>.jshdz before <name>.jshd, so no model changes and neither does a run. Measured at 24s versus 26s for two_trees, and xz -d returns the published bytes exactly. The harvest now also fails when a declared data file is not beside its model, which the field could not check while the data lived in an artifact. The page names the file as the repository stores it and says why the download beside it has a different extension: .jshdz is JVM-only, since the XZ decoder is not compiled through TeaVM, so the browser editor a reader uploads into can only take .jshd. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four jobs passed the tutorial data around: testPreprocessing uploaded it, and testGuideExamples, testAdditionalFeatures, and deployStatic each downloaded it. Now that the data is committed, all of that goes -- one upload, three downloads, and a cross-job dependency. testAdditionalFeatures never used what it downloaded: none of the four scripts it runs reads landing/preprocessed_data, and the ForeverTree test beside them reads the .jshd files already committed under paper/. testPreprocessing keeps running, because exercising `preprocess` against netCDF and GeoTIFF is worth doing. It just no longer decides what ships. deployStatic expands each committed .jshdz as it stages the guides, which also retires the hardcoded three-file copy: the guide directory names the destination, so adding a guide with data needs no change there. Verified locally that the step produces the same three paths with bytes identical to what the site serves today. 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.
The two data-driven guides declared what data they need, but the files were not in the repository.
They were preprocessed on every push from a zip fetched over the network —
landing/test_preprocess.sh:19does
wget https://joshsim.org/guides/raw_tutorial_supplement.zip, from the site the build publishes to.So the input to the build was a mutable URL. If that zip changes, the published guides change with
no commit and no diff. If it 404s, four jobs fail. This commits the data instead.
Why this is affordable
.jshdzis XZ-wrapped.jshd, and it compresses extraordinarily well:.jshd.jshdzprecipitationprecipitationTularetemperatureTulare34 KB against a 64 MiB pack. The repo already commits four
.jshdfiles underpaper/, socommitting data is not a new policy here — only the compression is.
Why nothing else had to change
MultiFormatExternalGetterresolves a bare external name to<name>.jshdzbefore<name>.jshd, soexternal precipitationfinds the compressed file with no model edits and no jar changes. Andbecause the committed name already matches the external name, the rename the old pipeline needed
(
precipitation_geotiff.jshd→precipitation.jshd) goes away.The one constraint
.jshdzis JVM-only — the XZ decoder is not compiled through TeaVM (build.gradle:47). Theguides tell readers to upload the data into the browser editor, so the site must keep serving
.jshd.deployStaticnow expands each committed file as it stages the guides, which also retiresthe hardcoded three-file copy: the guide directory names the destination, so adding a guide with
data needs no change there.
The page says this out loud rather than leaving a reader puzzling over a filename they cannot find —
the Data row names the file as the repository stores it and notes that the download is the expanded
form.
CI simplification
testPreprocessingpreprocess, uploads artifactpreprocess— no longer decides what shipstestGuideExamplestestAdditionalFeaturesdeployStatictestPreprocessingkeeps running: exercisingpreprocessagainst netCDF and GeoTIFF is worth doing.It just no longer decides what ships.
Also carried
The first commit declares the
data:field on both guides, which was missing — the manifest recordedthat the models reach
precipitationTulare/temperatureTulare/precipitationbut not which filesprovide them. The harvest now fails both when a runnable unit reads an external no declared file
provides, and when a declared file is not beside its model — a check the field could not have while
the data lived in an artifact. Conformance tests are exempt from the first; their fixtures are staged
by the test harness.
Verification
xz -dreturns the published bytes exactly — all three round-trip byte-identical to what thesite serves today
two_treestakes 24s from.jshdzagainst 26s from.jshdexamples/test_guide_examples.shpasses against the committed data, from a clean tree with nohand-staged files
declared-but-absent file each fail the harvest with a file and line
Known gaps
examples/test.shalso stages fromlanding/preprocessed_data/and is in no workflow — thesame shape as
paper/management/test.sh. Left alone; nothing fed it before this change either.landing/test_preprocess.shstill wgets the raw zip. That is now only in the test path, not thepublish path. Making it diff its output against the committed copy — so a preprocessing regression
fails the build instead of quietly changing the guides — needs byte-reproducible
preprocessoutput, which I have not verified.
deployStaticis skipped on PRs, so its rewiring first runs when this reachesdev.🤖 Generated with Claude Code