Library M10c: the tutorial crate¶
M10c is the third part of the tenth of the eleven milestones of #48, which turns Henad into a library published on crates.io. The finished code of the five first-model pages moves from
crates/henad-models/src/tests/tutorial/intoexamples/tutorial, the packagehenad-tutorial, which is a workspace member and never published. Its library and its tests name every item through thehenadfacade, and its two GPU models build their own shader copies through henad-build andhenad::include_shaders!, as a downstream crate does. A test holds each shader copy to the shipped one byte for byte, the parity tests move with the models, and a new test runs the testing kit over the five. The tutorial needed no item the facade lacked, but the authoring prelude gains ten names that the CPU trait signatures and field layers force on every model page. The five first-model pages and the authoring reference move to facade paths in the same change.
State before¶
48-library stood at 366a764 (M10b, the facade), with a clean tree.
The tutorial twins sat in crates/henad-models/src/tests/tutorial/ as test modules of henad-models, about 2,100 lines across eight files.
They imported from seven paths across henad-core and henad-compute, and the two GPU twins bound the shipped shaders through henad-models' own shader_bindings, under the names gpu_game_of_life and gpu_ants.
The pages printed crate::shader_bindings::gpu_life and gpu_foraging, so a twin and its page disagreed there.
The five pages' import snippets used henad-core and henad-compute paths, and the GPU pages' test snippets still called crate::tests::support::headless_context, which M10a had replaced with headless_test_device.
Record #40 flagged wrap_index, wrap_coord, axis_delta, offsets, for_each_neighbor and heading_octant as left out of the authoring prelude, for M10c to settle.
What was done¶
The crate¶
examples/tutorial holds Cargo.toml, build.rs, src/ and tests/.
- Manifest.
publish = false, the workspace's version, edition, MSRV, licence and lints. The one normal dependency ishenadby path, with no features. The build dependency ishenad-buildfrom the workspace. The dev-dependencies takehenadagain withexample-modelsandtesting, pluslogandrayon. [4.1]'s table gives henad-models by path as the dev-dependency. Taking the example models throughhenad::modelskeeps the tests on facade paths, as asked, and adds no edge the normal graph lacks. The rootCargo.tomlnames the member, asmembers = ["crates/*", "examples/tutorial"]. - Build script.
stamp_commit()andShaderBuild::discover("src")?.generate(), the template's two lines from [4.3.3]. A secondcargo build -v -p henad-tutorialreportsFresh henad-tutorial. - Library.
lib.rskeeps the oldmod.rsdoc, rewritten for the new home, thenhenad::include_shaders!(), the five modules, andmodels(), which registers the five under the crate's ownbuild_info!(), in the shape of the template'smodels()[4.3.4]. The files are where a page tells a reader to make them:life.rs,foraging/{mod,field}.rs,virus.rs,gpu_life/mod.rsandgpu_foraging/mod.rs, each moved withgit mv. - Imports. Each model file opens with
use henad::authoring::prelude::*;. The macros come from the root (henad::params!,henad::actions!,henad::buffers!,henad::agent_lanes,henad::for_each_chunk_mut), and the GPU agent vocabulary fromhenad::authoring(BufferSpec,Domain,PassCtx,NUM_AGENTS,split_paramsand the rest), as [8] asks of a GPU page.GRID_INIT_SEED,AGENT_INIT_SEEDandAgentLanescome fromhenad::authoringas well. The unit tests reach the states throughhenad::engine,SimStateandGpuSimStatethroughhenad::runner,wgputhroughhenad::gpu,StatEntrythroughhenad::stats, and the test device throughhenad::testing.grep -rn henad_ examples/tutorialfinds the crate's own name,henad_tutorial, in its tests,henad_buildinbuild.rs, andhenad_modelsin a comment of the copiedgpu_life/display.wgsl. - Shaders.
gpu_life/holds copies ofgpu_game_of_life/{step,display,reduce}.wgsl, byte for byte.gpu_foraging/holdsgpu_ants/{state,step,merge,display,reduce}.wgslwithgpu_ants::stateread asgpu_foraging::state, in the#define_import_pathofstate.wgsland the imports ofstep.wgslandreduce.wgsl. The action shadersclear.wgsl,randomise.wgslandreset_colony.wgslstay behind, since no page declares an action on a GPU model. The twins now bindcrate::shader_bindings::gpu_lifeandgpu_foraging, the paths the pages print.
The tests¶
| File | Tests | Origin |
|---|---|---|
src/life.rs |
a_blinker_rotates_and_comes_back |
Moved |
src/foraging/mod.rs |
results_do_not_depend_on_the_thread_count |
Moved |
src/virus.rs |
the_virus_walks_a_path_and_stops_at_a_resistant_node |
Moved |
src/gpu_life/mod.rs |
the_alive_count_matches_the_cpu_model |
Moved |
src/gpu_foraging/mod.rs |
a_run_replays_bit_identically |
Moved |
tests/parity.rs |
The 12 parity tests, the_game_of_life_tutorial_matches_the_shipped_model and its siblings |
Moved from src/tests/tutorial/parity.rs |
tests/shaders.rs |
the_gpu_life_shaders_equal_the_shipped_ones, the_gpu_foraging_shaders_equal_the_shipped_ones_apart_from_the_import_path |
New |
tests/kit.rs |
the_tutorial_models_conform |
New |
The parity tests became an integration test. They read nothing but public items, and an integration test is a second crate on the facade, so the parity test checks the facade from outside as well. The page-owned tests stay inline, since the pages include their files whole.
tests/shaders.rs reads both sides with include_str!, the shipped side through ../../../crates/henad-models/src/.
The gpu_foraging comparison replaces gpu_ants::state with gpu_foraging::state on the shipped side, compares the whole text, and asserts that the shipped shaders name gpu_ants::state exactly three times.
A fourth mention then fails the test where a blind replace would hide it.
tests/kit.rs is the template's every_model_conforms [4.3.4] with a warning when no adapter exists.
Every CPU model skips the four GPU checks and every GPU model skips ThreadCount, each as does not apply to this backend, and nothing else is skipped.
The facade¶
The tutorial needed no item without a facade path, so facade_paths.rs is unchanged.
henad::authoring::prelude gains ten names, each one a type the pages' CPU models cannot avoid naming:
| Added to the prelude | Where a model page names it |
|---|---|
Extent |
Every AgentModel and NetworkModel signature (from_params, init, DEFAULT_EXTENT) |
NoIndex, StepCtx |
type Index and run_step_pass of an agent model |
Nodes, NodeCtx, Network |
init, run_node_pass and stats of a network model |
ScalarFieldSpec, Combine |
A scalar field layer's trait and its COMBINE |
Deposits, ScalarRead |
run_deposit_pass and the read view of a scalar field |
wrap_index, wrap_coord, axis_delta, offsets, for_each_neighbor and heading_octant stay at henad::authoring::primitives::space, since no page uses them.
The GPU agent vocabulary stays at henad::authoring as [8] has it, and so do the engine's seeds and parameter indices.
Packaging and the licence page¶
- The
packagejob has excludedhenad-tutorialsince M1, andcargo package --workspace --exclude henad-tutorial --no-verify --lockedstill packages every crate. scripts/check_packaging.shreadscrates/*alone, and a comment now says that examples/tutorial never packages and reads the example models' shaders.about.tomlsets[private] ignore = true.docs/license.html, regenerated with cargo-about 0.9.1, is unchanged. Without that table the page would list henad-tutorial, which a run with the table removed confirmed.- henad-models' package no longer carries the tutorial, which sat under
src/and shipped in its tarball.
The docs¶
- Whole-file includes. The six of [7] point at
examples/tutorial/src/(life.rs,foraging/mod.rs,foraging/field.rs,virus.rs,gpu_life/mod.rs,gpu_foraging/mod.rs), each page's repository sentence names the new path and links to it on GitHub, and none of the six carries atitle=. The eight shader includes on the two GPU pages also point at the tutorial's copies. The GPU ants page then showsgpu_foraging::state, the import path the page writes, where it showedgpu_ants::stateunder the headinggpu_foraging/state.wgsl. The closing sentences say the shaders are copies of the shipped port's own. - The first-model pages. Each page's first import is
use henad::authoring::prelude::*;. An import snippet that only added prelude names became a sentence saying the prelude holds them, and one that added names outside it now shows only those (use henad::for_each_chunk_mut;,use henad::authoring::GRID_INIT_SEED;, the GPU vocabulary fromhenad::authoring,use henad::authoring::AgentLanes as _;). The macros readhenad::params!,henad::actions!andhenad::buffers!, and the tests' imports match the files. The two GPU test snippets callheadless_test_device(&TestDeviceRequest::baseline()), and the note under the GPU Life test explains it. The GPU ants page's note on itsas _imports now explains the one left, and its parameter note namesNUM_AGENTS,WORLD_WIDTHandWORLD_HEIGHTinstead ofcpu::agent_engine. reference/primitives.md. The example imports readhenad::authoring::primitives::{space, rng}, the two section lines give the Rust path and the WGSL module apart,NeighborhoodKindsits athenad::authoring::NeighborhoodKind, and "Related, elsewhere" namesSpatialHash,Extent,reduce_chunks,Networkandlabel_componentsathenad::authoring. Two new sentences say which primitives the authoring prelude holds and where the other space helpers come from.- Other pages.
authoring/performance.mdnameshenad::authoring::SpatialHash,authoring/agent-models.mdsaysSpatialHashandNoIndexare athenad::authoringand in the prelude, andauthoring/statistics.mdnameshenad::authoring::label_components. The sweep for inner paths a reader types also movedauthoring/network-models.md(Network),authoring/shaders.md(henad::authoring::primitives),authoring/determinism.md(henad::testingbehind the facade'stestingfeature, andhenad::install_panic_hook),reference/models.md(henad::models::example_models()) andreference/cli.md(henad::Simulation). Three inner paths are left in place on purpose, listed under issues below.
AGENTS.md and the changelog¶
- AGENTS.md: "The workspace has 8 crates, and the tutorial crate beside them", a
henad-tutorialline in the diagram's legend, the tutorial's dependencies after the dependency rule, ahenad-tutorialcrate bullet after the facade's naming its files, its import rule, its build script, the three test files and its packaging exclusions, and the foraging tutorial in the list ofresults_do_not_depend_on_the_thread_counttests. - CHANGELOG: two Added lines (the prelude's ten names,
examples/tutorial) and one Changed line (the guide and the reference onhenad::paths, the includes fromexamples/tutorial).
Edited tree¶
.
├── AGENTS.md ~ the tutorial crate, its tests, the crate count
├── CHANGELOG.md ~ the prelude, examples/tutorial, the docs on facade paths
├── Cargo.toml ~ examples/tutorial as a member
├── Cargo.lock ~ the henad-tutorial package
├── about.toml ~ [private] ignore = true
├── zensical.toml ~ nav entry #41
├── scripts/check_packaging.sh ~ a comment on what the crate list leaves out
├── crates/henad/src/lib.rs ~ ten names in henad::authoring::prelude
├── crates/henad-models/src/tests/
│ ├── mod.rs ~ no tutorial module
│ └── tutorial/ - moved to examples/tutorial
├── examples/tutorial/ + the package henad-tutorial
│ ├── Cargo.toml
│ ├── build.rs + stamp_commit and discover("src")
│ ├── src/
│ │ ├── lib.rs ← tests/tutorial/mod.rs, + include_shaders!, models()
│ │ ├── life.rs ← tests/tutorial/life.rs
│ │ ├── foraging/{mod,field}.rs ← tests/tutorial/foraging/
│ │ ├── virus.rs ← tests/tutorial/virus.rs
│ │ ├── gpu_life/mod.rs ← tests/tutorial/gpu_life.rs
│ │ ├── gpu_life/{step,display,reduce}.wgsl + copies
│ │ ├── gpu_foraging/mod.rs ← tests/tutorial/gpu_foraging.rs
│ │ └── gpu_foraging/{state,step,merge,display,reduce}.wgsl + copies
│ └── tests/
│ ├── parity.rs ← tests/tutorial/parity.rs
│ ├── shaders.rs + the copies against the shipped shaders
│ └── kit.rs + the kit over models()
└── docs/
├── guide/first-model/*.md ~ facade imports, includes from examples/tutorial
├── reference/{primitives,models,cli}.md ~ facade paths
├── authoring/{performance,agent-models,statistics,network-models,shaders,determinism}.md ~ facade paths
└── developing/agent-record/20261002-41-library-tutorial.md +
State after¶
Everything is uncommitted on 48-library, on top of 366a764, in the main checkout as asked.
Nothing is staged.
HENAD_REQUIRE_GPU=1 ./check.shpasses: 1067 tests, none failed, with the wasm32 typechecks, packaging, cargo-deny, the docs and the web build. M10b ended at 1064, and this session adds the two shader tests and the kit test. The 17 moved tests run in henad-tutorial in place of henad-models.uv run --locked zensical buildpasses with no issues, and the rendered pages carry theexamples/tutoriallistings.HENAD_REQUIRE_GPU=1 cargo test -p henad-tutorialruns 20 tests, all passing: the five inline, the kit, the 12 parity tests and the two shader tests.- A second
cargo build -v -p henad-tutorialreportsFresh henad-tutorial. cargo clippy -p henad-tutorial -p henad -p henad-models --all-targets --all-features -- -D warnings -W clippy::allpasses.cargo package --workspace --exclude henad-tutorial --no-verify --locked(with--allow-dirtyfor the uncommitted tree) packages every crate.cargo +1.95 check --workspace --locked, themsrvjob, passes with henad-tutorial in the workspace.
Proposed commit: feat: tutorial crate.
Issues found & future directions¶
- Three inner paths stay in the docs.
gpu-game-of-life.md:260showshenad_models::game_of_life::PALETTEin a WGSL comment the page copies from the shipped shader, and the copy has to stay byte for byte.developing/architecture.md:52describes where the planning code lives.authoring/network-models.md:282nameshenad_compute::cpu::layout, which a model never imports and which has no facade path. chunk_seedhas no facade path.reference/primitives.mdlists it among things a kernel reaches for, with its file. No model calls it today, sincerun_passand the engines seed each chunk. A model that draws insidefor_each_chunk_mut!would need it, and adding it tohenad::authoringthen is one line.- The pages still describe henad-models. Their snippet titles read
crates/henad-models/src/..., registration goes intoexample_models(), the run lines name-p henad-cliand--bin henad-app, and "Registering the model also opted us into the registry tests" points at henad-models' tests. The GPU Life page includescrates/henad-models/build.rs:shader_build. [8] rewrites all of it for the template in M11, and the finished files now match that template's shape already. - The prelude's glob shadows nothing today, but a page that defines a type named like a prelude name (
Network,Nodes) shadows the prelude's silently. A glob import loses to a local item, so this cannot break a build, only confuse a reader. - The CI
packagejob's manifest filter watchescrates/*/Cargo.tomland the root files, notexamples/tutorial/Cargo.toml. The job excludes the tutorial anyway, so nothing is missed. - The kit test leans on the device. Without an adapter and without
HENAD_REQUIRE_GPU, the kit skips the GPU checks with a warning, as the example models' registry test does. - Next. M10d builds the template on the facade, and its
votemodel follows the tutorial's import shape.