Some checks failed
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 8s
jouleclaw fabrics / cuda (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 15s
jouleclaw fabrics / vulkan (linux, RTX 4050 + Vulkan loader) (pull_request) Successful in 16s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m7s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m13s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 2m47s
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m16s
jouleclaw fabrics / metal (macOS, Apple GPU) (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Has been cancelled
`clippy::cast_ptr_alignment` flagged eight casts from `*const u8` to
more-strictly-aligned pointers. The previous commit allowed the lint
crate-wide with a note that GGUF aligns tensor data in practice. That
note was true and the reasoning was still wrong: these functions take
plain `&[u8]` slices and a `metal::Buffer`, and neither carries the
alignment the cast asserts.
let src_f32 = std::slice::from_raw_parts(src.as_ptr().cast::<f32>(), src.len() / 4);
`src` is an mmap'd region at whatever offset the file chose. Misaligned
`from_raw_parts` is undefined behaviour, not a slow load, and the
compiler is entitled to assume it cannot happen.
The three that looked safest were the least safe to reason about:
`contents()` on a buffer built with `new_buffer_with_bytes_no_copy` is
the mmap'd pointer itself, so "it's a Metal allocation, those are page
aligned" does not hold for them either.
All eight now decode byte-wise through `from_le_bytes` over
`as_chunks::<N>()`, which needs no unsafe alignment claim at all --
`u8` has alignment 1. The two staging loops keep one cast each, for the
destination, which really is a fresh Metal allocation; the comment says
which side is which and why. The crate-level allow is gone, and
`clippy -- -D warnings` passes without it.
Four unit tests, because the rewrite moved every byte index and a wrong
one would be silent:
- the bf16 and f32 buffer walks must agree with the scalar converters
element for element, over zero, ±1, pi, +inf, a NaN and f16's max;
- conversion from an odd offset on BOTH sides, which is the case the old
code could not express without UB;
- a trailing partial element is dropped rather than read past the end.
The metal CI job also now runs every target under `--features metal`.
It ran only `--test metal_kernels`, which left the crate's 46
metal-gated lib tests unrun -- a feature's unit tests being as invisible
to CI as its code was is the same mistake one level down. That job now
runs 48 lib tests and the 26 kernel tests.
Still outstanding from the same sweep: `mut_from_ref` on
`MetalBuffer::as_mut_slice(&self)`, which hands out a `&mut` from a `&`.
Co-Authored-By: Claude Opus 5 <[email protected]>
118 lines
5.1 KiB
YAML
118 lines
5.1 KiB
YAML
name: jouleclaw fabrics
|
|
|
|
# Every compute fabric gets built, on hardware that has the device.
|
|
#
|
|
# `metal` did not compile at all -- 3063 errors -- because no job ever built
|
|
# it: 40 architecture files carried `#[cfg(feature = "metal")]` blocks that had
|
|
# never once been compiled. An unbuilt feature does not stay still, it drifts,
|
|
# and nothing here would have noticed. So each fabric's error count is pinned
|
|
# and may fall but never rise.
|
|
#
|
|
# A baseline above zero is a debt, not a pass. Zero means the fabric compiles,
|
|
# which is still not the same as working: none of these runs a kernel yet.
|
|
#
|
|
# `rocm` has no job because `rocm = []` declares a feature with no dependencies
|
|
# and no code. It compiles perfectly while providing nothing, and a green check
|
|
# would report health it does not have.
|
|
#
|
|
# Nightly and path-filtered: the fleet's queue is the scarce resource, and these
|
|
# numbers move only when someone edits this crate.
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
schedule:
|
|
# 07:00 UTC daily. Forge cron is UTC.
|
|
- cron: '0 7 * * *'
|
|
pull_request:
|
|
paths:
|
|
- 'jouleclaw/jouleclaw-rs/crates/jouleclaw-omni/**'
|
|
- 'jouleclaw/jouleclaw-rs/Cargo.toml'
|
|
- 'jouleclaw/jouleclaw-rs/Cargo.lock'
|
|
- 'scripts/check-fabric-errors.py'
|
|
- 'scripts/check-metal-shaders.py'
|
|
- '.github/workflows/jouleclaw-fabrics.yml'
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
metal:
|
|
name: metal (macOS, Apple GPU)
|
|
# Metal is macOS-only, so this is the one runner that can build it.
|
|
runs-on: macos
|
|
timeout-minutes: 60
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
# launchd's PATH omits cargo on the macOS host runner.
|
|
- name: Put cargo on PATH
|
|
shell: bash
|
|
run: echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
|
- name: metal's error count may fall, never rise
|
|
shell: bash
|
|
run: python3 scripts/check-fabric-errors.py metal
|
|
# compile_pipeline builds every shader at runtime, so an MSL error is
|
|
# invisible to cargo. xcrun is the same frontend the runtime uses.
|
|
- name: every Metal shader source must compile
|
|
shell: bash
|
|
run: python3 scripts/check-metal-shaders.py
|
|
# Compiling a kernel says nothing about what it computes. This runner
|
|
# has a GPU, so the kernels are dispatched and checked against CPU
|
|
# references here.
|
|
- name: Metal kernels must compute the right thing
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
M=jouleclaw/jouleclaw-rs/Cargo.toml
|
|
# tests/metal_kernels.rs is behind `#![cfg(feature = "metal")]`. If
|
|
# that cfg ever stops holding the file compiles to nothing, the
|
|
# target runs 0 tests, and cargo exits 0 -- a green check for a
|
|
# suite that did not run. Count the tests before trusting the run.
|
|
n=$(cargo test --manifest-path "$M" -p jouleclaw-omni --features metal \
|
|
--test metal_kernels -- --list 2>/dev/null | grep -c ': test$')
|
|
echo "metal_kernels declares $n tests"
|
|
if [ "$n" -lt 26 ]; then
|
|
echo "::error::metal_kernels declares $n tests, expected at least 26."
|
|
echo "::error::Either the metal cfg stopped holding or tests were removed."
|
|
exit 1
|
|
fi
|
|
# Run every target, not just that one. `--test metal_kernels` left
|
|
# the crate's 46 metal-gated *lib* tests unrun: a feature's unit
|
|
# tests are as invisible to CI as its code was.
|
|
cargo test --manifest-path "$M" -p jouleclaw-omni --features metal
|
|
|
|
linux-fabrics:
|
|
name: ${{ matrix.feature }} (linux, RTX 4050 + Vulkan loader)
|
|
# The host runner, not the docker one: cudarc wants the CUDA toolkit at
|
|
# /usr/local/cuda and ash wants the system Vulkan loader, and this host has
|
|
# both beside an actual NVIDIA GPU. These features have only ever been
|
|
# built on macOS, where neither device exists.
|
|
runs-on: linux-hw
|
|
timeout-minutes: 90
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
feature: [cuda, vulkan]
|
|
steps:
|
|
# This host has no Node, so actions/checkout cannot run here.
|
|
- name: Fetch the source
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
want="${{ github.event.pull_request.head.sha || github.sha }}"
|
|
rm -rf src && mkdir src && cd src
|
|
git init -q
|
|
# Fetch the commit itself, not the ref: a later commit on the ref
|
|
# that this workflow's paths do not match would be fetched instead.
|
|
git fetch -q --depth 1 http://100.64.224.0:3400/Transaction-Science/open-standards.git "$want"
|
|
git checkout -q FETCH_HEAD
|
|
got="$(git rev-parse HEAD)"
|
|
[ "$got" = "$want" ] || { echo "::error::fetched $got, not $want"; exit 1; }
|
|
- name: ${{ matrix.feature }}'s error count may fall, never rise
|
|
shell: bash
|
|
working-directory: src
|
|
run: |
|
|
# nvcc is not on this host's PATH, and cudarc's
|
|
# cuda-version-from-build-system feature asks nvcc which CUDA this is.
|
|
export PATH="$HOME/.cargo/bin:/usr/local/cuda/bin:$PATH"
|
|
python3 scripts/check-fabric-errors.py ${{ matrix.feature }}
|