Skip to content

Merge pull request #2581 from JustVugg/fix/ui-agents-windows-host #4403

Merge pull request #2581 from JustVugg/fix/ui-agents-windows-host

Merge pull request #2581 from JustVugg/fix/ui-agents-windows-host #4403

Workflow file for this run

# CI: run the repo's own dependency-free gate (`make check` = clean + portable
# CPU build + C unit suites + Python stdlib tests) on the three claimed
# platforms. No model downloads, no CUDA, no external deps — by design (#140).
name: check
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
permissions:
contents: read
concurrency:
group: check-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
PYTHONUTF8: '1'
jobs:
linux:
name: Linux
runs-on: ubuntu-latest
# make check built serially and took 23 minutes on 2026-10-17, then past 25 with two
# more engines' GGUF code: the builds now use every core (make test runs the C and
# Python suites one after the other, so only the compiling is parallel).
timeout-minutes: 35
steps:
- uses: actions/checkout@v4
- name: make check
run: make -C c -j"$(nproc)" check
windows:
# The job that would have caught #68/#137 pre-merge: native MinGW-w64
# (MSYS2/UCRT64), the exact toolchain the README's Windows port targets.
name: Windows UCRT64
runs-on: windows-latest
# Measured, not guessed: this job now runs 19-21 minutes, so a 20-minute
# limit decided the result by runner luck. Three consecutive runs on
# 2026-08-27 took 19m21s (passed), 20m18s and 20m32s (both cancelled at the
# limit and reported as failures), including one on dev itself. The suite
# has grown - segment conformance and adapters, the edge runtime, per-engine
# cache indices - and MinGW is the slowest toolchain we build on.
# 2026-10-05: the 35 minutes set then were reached in turn. Over the last
# two days the job took 21 to 34 minutes when it passed, and was cancelled
# at 35 three times with every test passed so far (#1907, #1911 and a dev
# run), after #1901 added its suites. 60 leaves room for the suites still
# coming; a real hang still ends the job.
# 2026-10-20: reached again. The job took 45 to 57 minutes when it passed
# (#2265 twice, #2266), and was cancelled at 60 twice, once 5 seconds after
# its 2695 tests had passed (#2267). 90.
timeout-minutes: 90
defaults:
run:
shell: msys2 {0}
steps:
- uses: actions/checkout@v4
- uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2
with:
msystem: UCRT64
update: false
install: >-
make
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-python
- name: make check
run: make -C c check
# The Windows engine as this branch builds it. Small, and it makes an
# antivirus false-positive report (#527) verifiable on a PR instead of
# only after a release is tagged and published.
- uses: actions/upload-artifact@v4
with:
name: colibri-windows-exe
path: c/colibri.exe
k3-tiny:
name: Linux (generated tiny K3 vendor oracle)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: pip
cache-dependency-path: c/tools/requirements-kimi-k3-tiny.txt
- name: Install pinned tiny-fixture dependencies
run: python -m pip install -r c/tools/requirements-kimi-k3-tiny.txt
- name: Generate fixture and run vendor oracle
run: make -C c kimi-k3-tiny-check
mimo-tiny:
name: Linux (generated tiny MiMo-V2.6 vendor oracle, text + vision)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: pip
cache-dependency-path: c/tools/requirements-mimo-tiny.txt
- name: Install pinned tiny-fixture dependencies
run: python -m pip install -r c/tools/requirements-mimo-tiny.txt
- name: Generate fixture and run vendor oracle
run: make -C c mimo-tiny-check
- name: "Brio and the prompt cache: photos exact against a cold engine"
run: make -C c mimo-tiny-serve-check
- name: Same oracle under ASan and UBSan
run: |
make -C c clean >/dev/null
make -C c mimo EXTRA_CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer -g"
ASAN_OPTIONS=detect_leaks=0:detect_stack_use_after_return=0 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
python c/tests/mimo_tiny_harness.py --binary c/mimo --fixture c/mimo_tiny
cd c && ASAN_OPTIONS=detect_leaks=0:detect_stack_use_after_return=0 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
python -m unittest tests.test_mimo_prefix_serve tests.test_brio_serve.MimoBrioServe \
tests.test_systemone_api.MimoSystemOneEndToEnd
v4-tiny:
name: Linux (generated tiny V4 target oracle)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: pip
cache-dependency-path: c/tools/requirements-deepseek-v4-tiny.txt
- name: Install pinned tiny-fixture dependencies
run: python -m pip install -r c/tools/requirements-deepseek-v4-tiny.txt
- name: Generate fixture and run target oracle
run: make -C c deepseek-v4-tiny-check
glm53-oracle:
name: Linux (generated GLM-5.3 oracle contract)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: pip
cache-dependency-path: c/tools/requirements-glm53-tiny.txt
- name: Install pinned oracle dependencies
run: python -m pip install -r c/tools/requirements-glm53-tiny.txt
- name: Generate and validate oracle contract
run: |
python c/tools/make_glm53_tiny.py --output /tmp/glm53_tiny
python c/tools/check_glm53_checkpoint.py --model /tmp/glm53_tiny
# The dashboard's Brain and Profile tabs read EMAP/HITS/PROF from the
# engine's stdout. GLM-5.3-Flash emitted none of them and both tabs were
# blank (Discord, 2026-09-06). This drives the built engine through
# `coli serve` and the /experts and /profile endpoints on a streaming
# fixture, the same path the dashboard takes, so a family that stops
# emitting a line fails here and not on a user's screen.
- name: Build glm53 and check the dashboard contract
run: |
make -C c glm53
python c/tools/make_glm53_multimodal_tiny.py --output /tmp/glm53_mm
python c/tools/make_glm53_streaming_pair.py --fixture /tmp/glm53_mm --output /tmp/glm53_stream
python c/tools/make_edge_tiny_tokenizer.py --vocab-size 281 /tmp/glm53_stream-i4
cd c && COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_dashboard
COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_context_exceeded
COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_hostile_weights
COLI_GLM53_FIXTURE=/tmp/glm53_mm python -m unittest -v tests.test_glm53_hostile_weights.Glm53MatrixShapeTest
COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_cluster
# The two stdlib-only oracles used to be argparse scripts that `make
# test-python` collected as zero tests (#1700). They run here, on the
# fixtures generated above, token-exact against transformers in f32.
- name: Run the GLM-5.3 tiny oracles
run: |
cd c && GLM53_TINY=/tmp/glm53_tiny GLM53_MM_TINY=/tmp/glm53_mm python -m unittest -v tests.test_glm53_oracles
GLM53_TINY=/tmp/glm53_tiny COLI_GLM53_FIXTURE=/tmp/glm53_stream-i4 python -m unittest -v tests.test_glm53_prefill_batch
COLI_GLM53_FIXTURE=/tmp/glm53_mm python -m unittest -v tests.test_glm53_accept
qwen36-oracle:
name: Linux (generated qwen36 oracles)
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
cache: pip
cache-dependency-path: c/tools/requirements-glm53-tiny.txt
- name: Install pinned oracle dependencies
run: python -m pip install -r c/tools/requirements-glm53-tiny.txt
# Flint's layer (a sink per head, sliding-window layers, layers without RoPE, dense
# layers in front, sigmoid routing) token for token against tools/make_flint_tiny.py's
# float32 reference, the GGUF read in place with F32 and with Q8_0 experts.
- name: Build qwen36 and run the Flint tiny oracles
run: |
make -C c qwen36
cd c
for experts in F32 Q8_0; do
python tools/make_flint_tiny.py --out /tmp/flint_$experts --emit-ref /tmp/flint_$experts/ref.json \
--gguf /tmp/flint_$experts/model.gguf --gguf-experts $experts
COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/flint_$experts/model.gguf ./qwen36 16 8 /tmp/flint_$experts/ref.json
# the KV cache in float16, as a serve keeps it (a .json reference keeps float32 unless Q36_KV says)
COLI_CUDA=0 COLI_VULKAN=0 Q36_KV=f16 SNAP=/tmp/flint_$experts/model.gguf ./qwen36 16 8 /tmp/flint_$experts/ref.json
# the sliding layers' KV in a ring of window + 9 rows (the least spare), shorter than the run
COLI_CUDA=0 COLI_VULKAN=0 Q36_KV_RING_SPARE=9 SNAP=/tmp/flint_$experts/model.gguf ./qwen36 16 8 /tmp/flint_$experts/ref.json 2> ring.log
grep -q 'sliding-window layers keep a ring of 14 rows' ring.log || { cat ring.log; exit 1; }
done
# Muse Spark's layer (residual gates, the experts at their own width between two
# projections, a norm on every expert's output, capped logits) against
# tools/make_muse_tiny.py's reference, F32 experts and Q8_0 ones (the reference then
# quantizes the experts' inputs as the CPU's integer kernels do).
- name: Run the Muse Spark tiny oracles
run: |
cd c
python tools/make_muse_tiny.py --out /tmp/muse_F32 --emit-ref /tmp/muse_F32/ref.json --experts F32
COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/muse_F32/model.gguf ./qwen36 16 8 /tmp/muse_F32/ref.json
COLI_CUDA=0 COLI_VULKAN=0 Q36_KV_RING_SPARE=9 SNAP=/tmp/muse_F32/model.gguf ./qwen36 16 8 /tmp/muse_F32/ref.json 2> ring.log
grep -q 'sliding-window layers keep a ring of 14 rows' ring.log || { cat ring.log; exit 1; }
# Q8_0: the experts' inputs round to int8 from sums whose order differs from one CPU
# to the next (a code apart moved a 0.106 margin at the 16th token on AVX-VNNI), so
# this one is shorter and wider apart
python tools/make_muse_tiny.py --out /tmp/muse_Q8_0 --emit-ref /tmp/muse_Q8_0/ref.json --experts Q8_0 --max-new 8 --min-gap 0.2
COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/muse_Q8_0/model.gguf ./qwen36 16 8 /tmp/muse_Q8_0/ref.json
# LFM3's layers (short gated convolutions beside attention with a gate per head, the
# full layers' partial rotation, every branch normalised) against
# tools/make_lfm3_tiny.py's float32 reference, F32 matrices and Q8_0 ones.
- name: Run the LFM3 tiny oracles
run: |
cd c
python tools/make_lfm3_tiny.py --out /tmp/lfm3_F32 --emit-ref /tmp/lfm3_F32/ref.json
COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/lfm3_F32/model.gguf ./qwen36 16 8 /tmp/lfm3_F32/ref.json
COLI_CUDA=0 COLI_VULKAN=0 Q36_KV=f16 COLI_LOOKUP=0 SNAP=/tmp/lfm3_F32/model.gguf ./qwen36 16 8 /tmp/lfm3_F32/ref.json
COLI_CUDA=0 COLI_VULKAN=0 Q36_KV_RING_SPARE=9 SNAP=/tmp/lfm3_F32/model.gguf ./qwen36 16 8 /tmp/lfm3_F32/ref.json 2> ring.log
grep -q 'sliding-window layers keep a ring of 14 rows' ring.log || { cat ring.log; exit 1; }
python tools/make_lfm3_tiny.py --out /tmp/lfm3_Q8_0 --emit-ref /tmp/lfm3_Q8_0/ref.json --dense Q8_0 --max-new 8 --min-gap 0.2
COLI_CUDA=0 COLI_VULKAN=0 SNAP=/tmp/lfm3_Q8_0/model.gguf ./qwen36 16 8 /tmp/lfm3_Q8_0/ref.json
# LFM3-VL's tower (lfm3_vision.h) from an mmproj: a picture's rows within 1e-4 of the
# reference's, and the continuation they lead to
python tools/make_lfm3_tiny.py --out /tmp/lfm3_V --emit-ref /tmp/lfm3_V/ref.json --vision
COLI_CUDA=0 COLI_VULKAN=0 Q36_MMPROJ=/tmp/lfm3_V/mmproj.gguf Q36_VIS_DUMP=/tmp/lfm3_V/rows.bin SNAP=/tmp/lfm3_V/model.gguf ./qwen36 16 8 /tmp/lfm3_V/ref.json
python -c "import numpy as n; a=n.fromfile('/tmp/lfm3_V/rows.bin','f4'); b=n.fromfile('/tmp/lfm3_V/image_rows.f32','f4'); e=abs(a-b).max()/n.sqrt((b*b).mean()); print('LFM3 vision rows, worst error of the rms', e); assert a.shape==b.shape and e<1e-4"
macos:
# clang; libomp for the threaded path (Makefile falls back to
# single-threaded automatically if it's ever missing).
name: macOS (colibri + V4 platform gate)
runs-on: macos-latest
# `make check` here now takes about 14.5 minutes; at 15 the job was cut
# off inside the Python suite with no test failed, and showed as red.
# 2026-10-24 it took 24 minutes, and on the next run the suite printed
# OK at 25:00, the moment the job was cut off: 35 leaves the room again.
timeout-minutes: 35
steps:
- uses: actions/checkout@v4
- name: install libomp
run: brew install libomp
- name: make check (V4 execution gated off)
run: make -C c check