Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CI job graph

Owner: paul-heyse. Every job below has one owner, and every scheduled job has a documented response for when it goes red — a red scheduled job that nobody has agreed to answer is a notification, not a control.

Workflows: rust.yml, rust-scheduled.yml, python.yml, wheels.yml, solvers-image.yml, release.yml, docs.yml, governance.yml, register-review.yml, repo-hygiene.yml, plus the composite action .github/actions/setup-rust.

Common to every workflow: permissions: contents: read at the top, widened per job; per-ref concurrency with cancel-in-progress on pull requests only; CARGO_INCREMENTAL=0, CARGO_TERM_COLOR=always, RUST_BACKTRACE=1; tool versions as environment variables; the solver image referenced by digest (SOLVER_IMAGE=ghcr.io/paul-heyse/pse-solvers:ci-<hash>@sha256:<digest>); no RUSTFLAGS; sccache off; every action pinned by commit SHA with a version comment, kept current by Dependabot and pinact.

Required checks on main

Check contextJobRuns
rust / fmtfmtcargo fmt --all --check; taplo fmt --check
rust / clippyclippy (solver container)cargo clippy --workspace --all-targets --locked -- -D warnings, then again with --no-default-features
rust / testtest (solver container)cargo nextest run --workspace --locked --profile ci --features pse-relations/force-validate; cargo test --doc --workspace --locked --features pse-relations/force-validate; cargo test --benches -p pse-benches -p pse-relations --locked --features pse-relations/force-validate; JUnit artifact
rust / codegen-diffcodegen-diff (container: Ipopt headers + libclang)cargo xtask codegen --check
rust / family-checkfamily-checkcargo xtask family-check --evidence docs/capability-maps/evidence/rust/*.lock; cargo metadata --locked
rust / denydenycargo deny check; cargo audit; cargo shear
python / lintlintuv sync --locked --group quality --no-install-project; ruff format/check, pyrefly, import-linter, reuse lint, typos, taplo, uv lock --check, scripts/check_generated.py
python / testtest (3.11 and 3.14)maturin-backed uv sync; pytest -m "unit or component" -n auto --cov
python / parityparity (container ci stage; 3.13 on PRs, 3.11–3.13 nightly)editable native dev build, uv run --no-sync pytest --parity
docs / buildbuildscripts/adr.py index --check; mdbook build docs; lychee --offline; Pages artifact
governance / adr-lintadr-lintscripts/adr.py lint; scripts/check_register.py --lint; the needs-adr label rule; the IDAES tag in scripts/fetch-external.sh equals the parity pin
governance / pr-titlepr-titleConventional-Commit title check, types and scopes from cliff.toml

The full set above is declared in .github/setup/ruleset-main-full.json. just gh-setup applies it; just gh-setup-check verifies the live configuration. Check names are explicit job names, including the aggregate Python test result. A newly declared check must report before its ruleset is activated.

Run on pull requests, not required

rust / docs (RUSTDOCFLAGS=-Dwarnings cargo doc --no-deps), rust / doc-lint (paths-filtered), rust / coverage (cargo llvm-cov nextest … --lcov, artifact upload), rust / coverage-upload (host-runner Codecov OIDC, informational in phase 0), rust / semver (from the first tag; gating only on PRs labelled release), rust / msrv-floors (cargo hack check --rust-version, only when Cargo.lock changed), rust / test-macos and rust / test-windows (phase 1b, continue-on-error until a green week), governance / reuse, governance / agent-config, repo-hygiene / * (actionlint, zizmor, pinact). docs / deploy runs on main through actions/deploy-pages.

Scheduled jobs, and what to do when one goes red

rust-scheduled.yml runs weekly (cron 0 6 * * 1). None of these gates a pull request; each exists to find a problem before an upgrade does.

JobOwnerRed meansDocumented response
features-powerset (cargo hack --feature-powerset --depth 2)paul-heysea feature combination does not compile — most often an optional dependency used outside its #[cfg]Reproduce with the printed feature set, fix the cfg or the optional dependency, and add the combination to the PR-time --no-default-features clippy run if it is load-bearing. Open a kind/bug issue the same week; do not disable the job.
test-release (--cargo-profile release)paul-heysea test passes at dev optimization and fails at release — a float-sensitivity or an undefined-order assumptionTreat as a correctness bug, not a flake. Reproduce locally with just test-release, fix the test or the code, and record the cause; a release-only failure in numerics is exactly the class ADR-0030 and the force_validate feature exist to surface.
udeps (pinned nightly)paul-heysea declared dependency is unusedRemove it, or record it in tests/governance/tooling_deps.toml with a reason. During phase 0 the skeleton declares blueprint dependencies before using them, which is why this is scheduled rather than gating (register row R-18).
mutants on pse-ids and pse-mathir (continue-on-error)paul-heysesurviving mutants in the identity or IR crates — a test suite that does not discriminateRead the artifact, add tests for the survivors that matter, and note the ones deliberately left (a mutant in a Debug impl is not worth a test). Never treat the count as a gate.
unsafe-surface (cargo geiger)paul-heyseunsafe outside the four allowlisted crates, or a growth inside themCheck it against [workspace.metadata.pse] unsafe-allowlist; if it is genuinely new non-FFI unsafe, the Miri deferral’s trigger has fired (register row R-12) and the response is an ADR, not a suppression.
doc-lint (full)paul-heysea backticked a::b::c or [rustdoc:…] identifier in docs/** no longer resolvesUsually an upstream rename. Fix the document, or add the identifier to the allowlist with a reason. If a capability map is affected, regenerate its evidence rather than editing the claim.
floors-latest (cargo update --dry-run + floors against a temp lock)paul-heysea newer version of a pre-1.0 crate raises its declared MSRV above ours, or a family would go mixedThis is the early warning ADR-0018 is built around. Record it on register row R-07 with the date, and decide the upgrade deliberately — bump the toolchain by ADR, or hold the pin.
solvers-image-rebuild-check (no cache)paul-heysethe solver recipe no longer reproduces the same library checksumsDiff the build log against the last good one, find the moving input (a base-image digest, an upstream tarball, a compiler version), pin it in docker/solvers/, and update checksums.sha256. Until it reproduces, treat parity iteration counts as unverified (register row R-08).

solvers-image.yml builds the solver container on every pull request that touches docker/solvers/** and, on main, pushes it to GHCR and opens the pull request that moves the SOLVER_IMAGE pin (a digest reference inside the workflow files, so a checkout is reproducible on its own). Opening that pull request needs the PIN_PR_TOKEN secret: a fine-grained personal access token scoped to this repository with contents, pull requests and workflows write, because the default GITHUB_TOKEN cannot create or update files under .github/workflows. Without the secret the job succeeds, uploads the image manifest, and prints the exact just solver-pin-update --ci … --dev … command (register row R-21). The canonical pins are in .github/setup/solver-images.json, outside the solver recipe tree hash. just solver-pin-check checks every literal consumer. just solver-rebuild-check repeats the no-cache library comparison locally.

register-review.yml runs monthly with issues: write, executes scripts/check_register.py --due — including the shell checks in the check column — and opens or updates one Register review YYYY-MM issue. Red means the workflow itself failed, not that a check reported something: --due reports a failing command and never fails on it. The response is to run it locally, then to act on each due row in the issue before closing it.

codecov.yml is informational, with after_n_builds equal to the number of uploads; coverage never blocks a merge in phase 0.

Local equivalents

CI tierLocal recipeWhat it proves
fast feedbackjust ci-fast (fmt-check, check, clippy, test, doctest)The workspace compiles and the unit and component tests pass at dev.
pull-request gatejust ci-pr (adds governance, policy, docs, bench-smoke, quality, adr-lint, py-test)Local Rust/Python and repository checks; container parity is separate.
parity preflightjust parity-containerExisting IDAES/solver preflight cases in the pinned dev image, using a separate Python 3.13 environment.
scheduledjust features-powerset, just test-release, just udeps, just mutants-file, just unsafe-surface, just floors-latestIndividually reproduces one weekly job.

just --list is the contract: if a check exists in CI and has no local recipe, that is a bug in the justfile.

Phase-zero limits

Code generation and API-reference doc lint remain deferred (register R-20). PR-time API doc lint emits a deferral notice when the path indexes are absent; its CLI remains an exit-2 stub and scheduled checks retain the register trigger. codegen-check detects staged, unstaged and untracked generated-path changes; it does not prove a real schema or binding generator exists. The parity suite currently qualifies its environment and package boundaries, not numerical model parity.

The wheel workflow runs only by manual dispatch (just wheels-check <ref>) or from the release workflow. It builds all five native platform targets, checks cp311-abi3 tags, installs each wheel under Python 3.11 and 3.14 outside the source tree, and builds/installs the sdist.

Development compile path

just py-sync uses maturin’s editable dev profile from pyproject.toml; dependency optimization stays in Cargo’s dev profile and the local build cache is reused. Follow it with just py-test and just quality. just parity-container uses the same editable profile with the pinned solver image and a separate Python 3.13 venv. Automatic Python CI follows this path, with no dependency on a wheel or sdist job. Rust release-profile checks remain scheduled, and distribution acceptance is manual or release-time. Source changes, dependency changes and toolchain changes therefore run development checks without automatically rebuilding five distribution targets. Solver recipe changes retain their separate container build/linkage checks.