# Consolidated CI and release matrix MetaCrate has one routine code-ready signal: the `required` job in `.gitea/workflows/ci.yml`. It runs for every push and pull request on `ubuntu-latest`, has a hard 15-minute timeout, and cancels an older run for the same branch or pull request. `.gitea/workflows/release.yml` is the only other workflow. It is manual, weekly, and tag-only; it never runs for an ordinary source push or pull request. The reviewed coverage map is `ci/ci-coverage.json`. It maps every responsibility from the former 13 workflows to the required gate, the non-routine release gate, or both, with a rationale. `metacrate-ci-matrix ci-audit` rejects missing routes, additional workflow fan-out, non-Ubuntu jobs, Skia source-build fallback, and changes to the 15-minute limit. ## Required gate The Rust `required-gate` command owns the command graph and records the elapsed time and result of every stage. YAML performs checkout, one package install, one coherent persistent-cache inspection, pinned toolchain/tool setup, and exact native-cache preparation before invoking it. Fast-failure order is: 1. formatting, deterministic generators, API mappings, parity catalogs, and milestone ownership checks; 2. regenerated API, documentation, dependency, and provenance evidence; 3. exact Skia ARM64 GNU binary installation proof; 4. all-feature workspace library/integration tests, minimal and JPEG 2000 feature isolation, compatibility tests, and concurrency regressions; 5. offline/fake program smoke and downstream API/SemVer consumers; 6. Clippy, warning-free documentation, dependency policy, unused dependency, provenance, and committed performance-evidence audits. The driver stops starting work after its 12-minute internal target. This leaves three minutes for shutdown and artifact upload beneath the workflow's hard limit. `CARGO_BUILD_JOBS=2` is the bounded ARM64 setting; it permits useful parallelism without the repeated high-memory compiles caused by the former 13-job fan-out. One feature-unified product/compatibility test graph uses a single Cargo job, with package-qualified core features so unrelated consumer options are not activated. This is the sole memory-safety exception, while every other gate command retains the two-job bound. Run the structural audit locally on Linux, Windows, or macOS with: ```sh cargo run --locked -p metacrate-ci-matrix -- ci-audit ``` The full required graph expects the CI native-cache environment. On an ARM64 GNU Linux host with OpenJPEG installed by `tools/install_openjpeg_2_5_4.sh` and the pinned Skia archive available, reproduce it with: ```sh OPENJPEG_PREFIX=/path/to/openjpeg \ METACRATE_EXPECTED_HOST=aarch64-unknown-linux-gnu \ METACRATE_SKIA_ARCHIVE=/path/to/skia-binaries-a25a0fdb7d90429aa2d1-aarch64-unknown-linux-gnu-jpegd-jpege-pdf-svg-textlayout-vulkan-webpd-webpe.tar.gz \ cargo run --locked -p metacrate-ci-matrix -- \ required-gate --evidence artifacts/ci/local-required.json ``` The driver is shell-free and constructs child processes with argument arrays, so command and path mappings are portable. Gitea jobs intentionally remain Ubuntu-only. Windows GNU and macOS portable surfaces are cross-checked from Ubuntu by the non-routine release graph; no native runtime claim is inferred. ## Cache identity and invalidation The dedicated VPS runner's persisted tool-cache mount stores Cargo registry/git downloads, the two pinned audit binaries, the routine target directory, and native archives/installations. The target directory is content-keyed by Rust 1.97.1, `aarch64-unknown-linux-gnu`, `Cargo.lock`, the CI coverage and dependency policies, the Skia feature manifest, and the OpenJPEG installer. Changing any of these selects a new target generation. This avoids depending on an external GitHub cache service that is unreachable from the isolated Gitea runner. After checkout, a state file beside the keyed target cache binds every tracked path's Git mode and actual-content digest to the exact mtime used for its last build. Unchanged files recover that mtime across commits and retain valid Cargo fingerprints. A content or executable-mode change receives the current time, so it is newer than cached outputs and invalidates the affected graph. The state is written before compilation, so completed work remains reusable after a timeout, and the workflow immediately verifies every applied mtime. The manual `cold_cache` dispatch input clears the Cargo registry/git dependency cache, pinned tools, and exact native paths before setup; it intentionally keeps the separately keyed compiled target cache. This is the acceptance test's cold dependency/native-cache case. Ordinary pushes and dispatches reuse all validated cache layers. OpenJPEG caches include a three-line identity containing version 2.5.4, commit `6c4a29b00211eb0430fa0e5e890f1ce5c80f409f`, and runner architecture. The installer checks this identity and `pkg-config --exact-version` before reuse. The Skia cache is the exact 0.99.0 ARM64 GNU feature archive with SHA-256 `dd127f458a5e67a79f3936a8aa19f822fe90a1d6a11b50b5f84df2b0519d909c`. The workflow never sets `FORCE_SKIA_BINARIES_DOWNLOAD`: rust-skia uses the normal crates.io `.cargo_vcs_info.json` hash. The proof reads rust-skia's persisted build-script output even on a cache hit and requires `DOWNLOAD AND INSTALL SUCCEEDED`; the Rust driver rejects missing success output or any `STARTING A FULL BUILD`. The adapter enables rust-skia's `no-compile` feature on every target, making an unavailable or incorrect binary a fast failure rather than a source build. To invalidate local artifacts, remove only the relevant target directory or use: ```sh cargo clean --target-dir target/required cargo clean --target-dir target/release-gate ``` ## Non-routine release gate The Rust `release-gate` command owns clean MSRV and portable cross-target checks, benchmark compilation, the 16-cycle resource soak, all 16 source packages, release binaries, native artifact inspection, and the aggregate release-candidate audit. Reference C# performance regeneration and live-grid, physical-audio, proprietary-service, signing, and platform-native installer checks retain their documented credential/device/manual boundaries. Their committed evidence and fast regression checks remain in routine CI. ## Troubleshooting and runtime evidence - A host mismatch means the required runner label is not the documented ARM64 GNU VPS. Do not substitute a musl or x86 archive. - An OpenJPEG identity failure requires deleting only its native cache entry and rerunning the installer. - A Skia hash/download failure indicates an upstream release-asset or redirect reachability problem. Restore the exact archive in the configured cache; do not enable source compilation. - A 12-minute driver failure identifies the last completed stage in the uploaded `required-gate.json`. Optimize or remove duplicate work only after updating the coverage manifest; do not raise the 15-minute timeout. The validation record and five qualifying ARM64 run links are maintained in [`ci-runtime-evidence.md`](ci-runtime-evidence.md).