Contributing¶
The binding rules for every contributor, human or AI agent, are in
AGENTS.md.
The short version:
- Never lose a case: dictionary edits are lossless; nothing is deleted without an explicit command.
- Never crash: no
unwrap,expect,panic!orunsafeoutside tests; case files and solver processes are hostile input. - Everything is a command, and the layering is enforced.
- The solver is the spec. Read the urbanMicroclimateFoam source freely; ported code keeps its copyright line and names its source file and commit.
- Pull requests target
main, never another pull request; each push is first reviewed by someone other than its author, a CodeRabbit review must cover the head, and the maintainer merges (AGENTS.md §8).
The solver source¶
solver/ is a git submodule: the urbanMicroclimateFoam repository, branch master (the
OpenFOAM-12 port), pinned to the commit umcfGui was last checked against. Read it freely, it is
the specification. Every version is a tag in the same clone:
git submodule update --init # after a plain clone
git -C solver show of-org_v8.0:urbanMicroclimateFoam.C # the OpenFOAM-8 version of a file
git submodule update --remote solver && git add solver # move the pin to the solver's latest master
cargo xtask catalog # regenerate the catalog for the new pin
After moving the pin, the diff of docs/solver-coverage.md shows what the solver gained or lost:
new types, removed keywords, changed defaults.
Never commit inside solver/ from this repository: changes to the solver are pull requests to
urbanMicroclimateFoam.
Moving the pin is a commit of its own; check it with cargo xtask ci and the
tests against real cases.
Quality gates¶
cargo xtask ci runs every gate, stopping at the first failure:
| Gate | Checks |
|---|---|
fmt |
cargo fmt --check |
clippy |
lints as errors, with and without default features |
test |
all tests, with and without default features |
layers |
the crate layering (Architecture) |
catalog --check |
the solver catalog matches the pinned solver/ (needs the submodule and its tags) |
assets |
every file under assets/ and docs/images/ has a row in ATTRIBUTION.md |
wasm |
the L0–L3 crates build for wasm32-unknown-unknown |
docs |
the API reference builds without warnings, and the site builds in strict mode |
deny |
every dependency's licence is GPL-3-compatible (needs cargo install cargo-deny) |
CI also runs cargo xtask make-examples --check, outside cargo xtask ci because it fetches
the tutorials from GitHub (Example cases).
Reviews¶
Every pull request gets a CodeRabbit review; main requires its check and cargo xtask ci to
pass, and every review thread to be resolved. A Claude Code review runs only when asked for,
never on its own: comment /claude-review on an open pull request. Only a comment from an
account with write access to the repository counts. It reviews the PR's head as it stands,
posts its findings as inline comments (each a thread to resolve before merging) and a summary
comment, and never runs the PR's code. The comment trigger runs the workflow as it is on
main. It needs the repository secret CLAUDE_CODE_OAUTH_TOKEN (an OAuth token of a Claude
Pro or Max subscription from claude setup-token, then gh secret set
CLAUDE_CODE_OAUTH_TOKEN); the reviews count against that subscription's usage limits.
This site¶
The site is built with Zensical from docs/ and zensical.toml:
uv venv --python 3.12
uv pip install -r requirements_docs.txt
.venv/bin/zensical serve # live preview on http://localhost:8000
cargo xtask docs builds it in strict mode (broken links fail) when Zensical is installed,
either on the PATH or in .venv/. Every page under docs/ must be in the nav of
zensical.toml.
Tests against real cases¶
Real case files are never committed (the small example cases are made for the repository). Tests that read them are opt-in through environment variables, and report a skip when they are unset:
| Variable | Test |
|---|---|
UMCF_CASES |
case directories (:-separated): every dictionary round-trips; every solver case has a version and regions |
UMCF_CASES_EXPECT |
pins versions: <case path>=8,<case path>=12,… |
UMCF_DICT_TREES |
directory trees, e.g. the OpenFOAM-8 and OpenFOAM-12 tutorials/ and etc/: every file with a FoamFile header round-trips |
UMCF_OF8_TUTORIALS, UMCF_OF12_TUTORIALS |
the snappyHexMesh tutorials get their version |
UMCF_FIELDS_ALL_TIMES |
with UMCF_CASES: read the fields of every time directory, not only the first and last |
UMCF_FIELD_PAIR |
<ascii case>:<binary case>: a copy converted with writeFormat binary; and foamFormatConvert -region <r> must read back bit for bit the same, fields and meshes |
UMCF_FUZZ_ITERS |
more iterations for the dictionary and field fuzz tests |
UMCF_DICT_TREES=$HOME/OpenFOAM-8/tutorials:$HOME/OpenFOAM-12/tutorials cargo test -p umcf-foam-dict --test corpus
Example cases¶
examples/of8 and examples/of12 hold small cases of the solver's features (the street canyon
with air only, with walls and street, with grass, with a tree), made from the urbanMicroclimateFoam
tutorials
(examples/README.md).
Every test run checks them: each is a case of its directory's version, the check finds no error and
no warning about the scripts, every dictionary round-trips. That they run needs OpenFOAM and
urbanMicroclimateFoam:
cargo xtask examples # all of them, each on a temporary copy, about 20 s
cargo xtask examples of12/streetCanyon_CFDHAM
It uses UMCF_OF8_BASHRC and UMCF_OF12_BASHRC, else ~/OpenFOAM-8/etc/bashrc and
~/OpenFOAM-12/etc/bashrc, and skips a version without one. Run it before merging a change to
the examples, and after building a new solver version.
They are made by cargo xtask make-examples from the tutorials at pinned commits: change the
generator (xtask/src/make_examples.rs), not the files, and run it again.
cargo xtask make-examples --check (in CI) fails when examples/ is not what it makes. It reads
the tutorials from UMCF_TUTORIALS (a clone), else fetches them from GitHub.
Commits¶
One task id per commit (M0.4: streamed field reader), only green states.