Repository navigation
Conversation
No CI job built the node image, started a cluster, or pulled and served an engine: every engine path in the tests was a stub, so an engine bump (bot or human) merged without ever running. The canary checks each bump at runtime before it merges. canary.yml (name Canary) runs nightly at 03:29, on pull requests that touch a pin, the launcher or the canary, and on dispatch (engines: ids or auto; attempt). It is read-only, uses no secrets and runs only on GitHub-hosted runners; one job may write, to dispatch the workflow again after an isa-skip. - runtime (vllm-cpu, ovms-cpu, llama-cpu; eat-vllm-cpu informational): pull the pinned image, serve a pinned Qwen3-0.6B the way the launcher runs it, then one completion (200, finish_reason stop or length, tokens, /Paris/), one stream (2+ chunks, then [DONE]) and the version the server reports against the pinned tag. A golden mismatch keeps hard and clears exact. - surface (every engine image): each flag and VLLM_* name the launcher and console/model_opt.py emit (canary/flags.sh) exists in the image, read from its own --help or, for vLLM, statically from arg_utils.py, cli_args.py and envs.py inside it; and the constants derived from the image (plan.py's vLLM versions, the NPU driver and firmware date) still match. - node: build the Dockerfile, k3d cluster create on it with the k3d release packaging/install.sh pins, and see the node Ready, kube-system up, every baked manifest and every DaemonSet they define. canary/engines.toml points at each pin with a locator (exactly one match, name@sha256 or name:tag@sha256), never a copy of it. In auto mode canary/plan.py runs only the engines whose pin, flags, settings, golden or derived constants changed against the merge base; the report job writes the canary-result artifact (canary-result.json) and is green only when every selected engine passes with hard true. Tests: tests/canary_test.sh (run.sh against tests/openai_stub.py and tests/fake_ovms.py behind a docker shim), tests/canary_spec_test.sh (the locators on the real tree, flags.sh, planning against fixture diffs and a git base, the report, the workflow's shape) and tests/canary_surface_test.py (the parser on vLLM v0.32.0's real sources and the pinned images' help output), wired into ci.yml.
run.sh read a missing probe.json as no failures, so a probe.py that crashed before writing its result passed. Once the engine is started, a pass now needs the probe's result; tests/canary_test.sh covers a probe that cannot parse its timeout.
…g builds match The first hosted canary run failed three engines, none of them a real break. canary/node.sh was committed without its executable bit, so the node job stopped with "Permission denied". It is now executable, and tests/canary_spec_test.sh checks every canary/ script the workflow runs. sglang-xeon reported --tp missing. bench/h54 passes --tp 1, and sglang's parser is a plain argparse.ArgumentParser that allows abbreviations: in the v0.5.22-xeon image --tp parses to tp_size 1, as sglang's own docs write it. For sglang, a long flag now also matches when it begins exactly one option in the help, and the detail names the expansion. vLLM flags still match exactly. llm-scaler reported vLLM 0.26.1.dev0+g568afb3a1.d20260907.xpu against the tree's 0.26.0. Commit 568afb3a1 is vLLM's v0.26.0 tag, and setuptools-scm names a tag built with uncommitted changes X.Y.(Z+1).dev0+g<commit>.d<date>, so the image is v0.26.0 with Intel's patches and the derived constant is right. release() now reads that form as the tag. A build commits after a tag still mismatches. The vLLM CPU goldens are recorded from the same run, and run.sh logs the runner's CPU model and count so a golden can name it. The sglang help output and version files are new fixtures, taken from the pinned image.
…thout NVIDIA The node canary built the image and started a cluster, but failed on the baked NVIDIA device plugin: on a runner without NVIDIA its pod starts under the nvidia runtime with no driver and crash-loops. The launcher never lets that happen. On a host without NVIDIA its cluster create labels the node nvidia.com/gpu.present=false, which the plugin's affinity excludes, and it always passes --no-lb and disables traefik. node.sh now passes those three arguments (NODE_ARGS). Both Intel stacks stay enabled so every baked manifest is still applied and checked. tests/canary_spec_test.sh checks that scripts/k3d-gpu still passes each argument word for word, so the canary cannot drift from the launcher.
… holds node.sh checked only kube-system, and let a pod a baked DaemonSet owns stay Pending. NFD runs in its own namespace, so an NFD bump whose master crash-looped passed, and an intel-gpu-plugin pod whose image did not pull stayed Pending and passed too. Device plugin, NFD and DRA bumps auto-merge on the node canary, so a pass has to mean the pods run. Every pod in every namespace must now be Running with its containers ready, or Succeeded. A plugin that needs hardware gets no pod on a hosted runner (its node selector keeps it off), so a pod that exists must run, and one still Pending at the timeout fails. Once up, the pods must hold for CANARY_NODE_SETTLE seconds (default 20) with the same restart counts, so a container that crashes after it started is caught; one restart while the cluster settles still passes. tests/canary_node_test.sh runs node.sh against a docker, k3d and curl shim with scripted pod states: up, NFD crash-looping, a plugin pod stuck Pending, a late pod, a flapping container, one restart, a missing DaemonSet, no pods and a failed cluster create. It is wired into the canary-tests job.
canary/plan.py reads bench/h54/h54.py (flags.sh takes sglang-xeon's flags from its sglang_args), but canary.yml's pull_request paths left it out, so a change there selected sglang-xeon in the plan yet never started the workflow. tests/canary_spec_test.sh now derives every file the plan reads (BASE_PATHS and each engine's locator, derived, flags_source and node paths) and checks that the pull_request paths cover each one.
CI on this branch went red once main moved EAT's vLLM to v0.28.0: tests/canary_spec_test.sh edited `tag: "v0.24.0@`, which no longer matched, so the plan selected nothing and the case failed. The same holds for every engine pin the tests named. The stubs in tests/canary_test.sh reported llama.cpp b11960, vLLM 0.32.0, sglang 0.5.22 and llm-scaler's 0.26.0, and the spec test edited or asserted sglang's v0.5.22-xeon tag, vllm-xpu's 0.32.0, the OVMS pin's form and the llama-cpu model revision. A tree with those pins bumped the way a freshness bump moves them failed 17 cases in canary_test.sh and 7 in canary_spec_test.sh, so every llama.cpp, vLLM CPU, sglang, vLLM XPU, llm-scaler or EAT bump would have been red in CI before its canary ran. The stubs now report the version each pin names (canary/plan.py show): the llama.cpp build, the vLLM CPU and EAT tags, the OVMS tag when the pin has one, sglang's tag written into a copy of the v0.5.22 fixture site, and llm-scaler's setuptools-scm version built from the derived constant. The spec test's edits match any value, the reference-form case rewrites the OVMS pin to both forms whatever the tree holds, vllm-xpu's derived constants must agree on one value, and the cache key is checked against the engine's model and revision. An edit that changes nothing in the tree now fails as such, instead of reading as "selected nothing". Both suites pass on this branch, on it merged with main, and on a copy with the llama.cpp, vLLM CPU, sglang, vLLM XPU, llm-scaler, EAT and OVMS pins and the llama-cpu model revision bumped.
main added the known-issues job at the end of ci.yml (#256) where this branch adds canary-tests, so the two conflicted and gate_merge.sh's sync stops on a ci.yml conflict. Both jobs are kept as they are: known-issues as main has it, then canary-tests at the end of the file.
…ference scripts/images-lock pins reads `AGENT_OVMS_IMAGE="openvino/model_server` in the two seds that rewrite the OVMS pin's form as an image reference with no digest, which failed tests/image_pins_test.sh. The seds now match the name by pattern, so nothing in them reads as a reference.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Runtime and surface canary for engine and node images (freshness part P5).
No CI job built the node image, started a cluster, or pulled and served an engine. Every engine path in the tests was a stub, so an engine bump from a bot or a person merged without ever running. This adds
.github/workflows/canary.yml(nameCanary), which checks each bump at runtime before it merges.This PR's own Canary run on 5be20ca (run 40980204951) is green for all 15 engines, each with verdict
pass,hardtrue andexacttrue. That covers node, llama-cpu, ovms-cpu, vllm-cpu and every surface engine.What it checks
vllm-cpu,ovms-cpu,llama-cpu;eat-vllm-cpu(informational)/Paris/), one stream (2 or more chunks, then[DONE]), and the server's version against the pinned tag. A golden mismatch keepshardand clearsexact.vllm-xpu,llm-scaler,gaudi-vllm,llama-sycl,llama-vulkan,llama-full,dlstreamer,sglang-xeon,npu-umd,ovms, plus the runtime engines firstVLLM_*name that the launcher andconsole/model_opt.pyemit (canary/flags.sh) exists in the image. They are read from the image's own--help, or for vLLM statically fromarg_utils.py,entrypoints/**/cli_args.pyandenvs.pyinside it, without importing them. The constants derived from the image still match:console/plan.py's vLLM versions, the launcher'svver,NPU_UMD_DRIVERagainst the image'sintel-level-zero-npu, andNPU_UMD_FW_DATEagainst its firmware.nodek3d cluster create canary --image ... --waitwith the k3dpackaging/install.shpins (sha256-checked) and the arguments the launcher passes on a host without NVIDIA. Within 5 minutes: node Ready, kube-system pods up, every baked manifest present and every DaemonSet they define.How it runs
engines: ids orauto;attempt).canary/engines.tomllocates each pin in the tree and never copies it. Each locator must match exactly one ref, in either thename@sha256or thename:tag@sha256form. Inautomode,canary/plan.pyselects an engine only when, against the merge base, any of these changed: its resolved ref, its table, its golden, a derived constant, or a flag thatflags.shnames for it. A Dockerfile,share/or k3s change selects the node. A change to the harness selects everything.canary-result.json(artifactcanary-result) with exactly the interface schema. It is green only when every selected non-informational engine has verdictpasswithhardtrue.actions: write: it dispatches the workflow again (attempt + 1, at most 3) when an engine isisa-skip.flags.shattributes each flag literal inmodel_opt.pyandfit.pyto its engine through theengine == "X"tests around it. A literal that no rule attributes failsflags.sh, so a new emitter cannot go unchecked.Fixes from the first hosted run
The first Canary run (40960775877, on 0163539) failed three engines. None of them was a real break; each was a gap in the canary.
nodecanary/node.sh: Permission deniedtests/canary_spec_test.shchecks everycanary/script the workflow runs.node(second run)CrashLoopBackOffk3d cluster createon a runner without NVIDIA starts the baked plugin under the nvidia runtime with no driver. The launcher never does that: on a host without NVIDIA it labels the nodenvidia.com/gpu.present=false, which the plugin's affinity excludes, and it always passes--no-lband disables traefik.node.shnow passes the same three arguments (NODE_ARGS). The spec test checks thatscripts/k3d-gpustill passes each one word for word. Both Intel stacks stay enabled, so every baked manifest is still applied.sglang-xeon--tpnot in the imagebench/h54passes--tp 1. sglang's parser is a plainargparse.ArgumentParser, which allows abbreviations, so--tpis--tp-size, the only option it begins. For sglang, a long flag now also matches when it begins exactly one option in the help, and the detail names the expansion. vLLM flags still match exactly, so a renamed flag cannot hide behind a longer one.llm-scalerconsole/plan.py says 0.26.0, the image's package is 0.26.1.dev0+g568afb3a1.d20260907.xpuv0.26.0tag. setuptools-scm names a tree at a tag with uncommitted changesX.Y.(Z+1).dev0+g<commit>.d<date>, so the image is v0.26.0 with Intel's patches, and the derived 0.26.0 is right.release()now reads that form as the tag. A build made commits after a tag (.dev1and up) still mismatches.The vLLM CPU goldens (
exactwas null) are now recorded from that run.run.shalso logs the runner's CPU model and count, so a golden can name the CPU it was recorded on.Spike (this PR's own canary runs)
Run 40980204951 on 5be20ca is all green. It took 14 min 45 s from the first job start (00:31:22 UTC) to the end of the report (00:46:07). The critical path was vllm-cpu on an AVX2-only runner. Runner queueing comes on top of that when the PR's CI runs at the same time.
Runtime
vllm-cpuvllm/vllm-openai-cpu:v0.32.0vllm-cpu(run 40979011371)eat-vllm-cpuvllm/vllm-openai-cpu:v0.24.0ovms-cpuopenvino/model_server:2026.4.1-gpu, CPU plugin, int8llama-cpughcr.io/ggml-org/llama.cpp:server-b11960, Q8_0--dtype=bfloat16,--max-model-len=2048and--max-num-seqs=1;VLLM_CPU_KVCACHE_SPACE=1and a 2 GiB shm, which gave a KV cache of 9,344 tokens._C_AVX2on an AVX2-only runner. There its engine init (profile, KV cache, warmup) took 290 s.' Paris. The capital of France is also'came back on the AVX2 runner and on the AVX-512 runner.' Paris. The capital of Italy is Rome'.Node. The image built in 37 s, and the cluster was created in 10 s. It was ready 50 s later, with 11 baked manifests, 11 DaemonSets and every kube-system pod up. The job took 94 s.
Surface. Each row gives the image size against free disk.
llm-scalervllm serve --help=allneeds a devicevllm-xpullama-syclgaudi-vllmsglang-xeon--tpis--tp-sizellama-fullconvert_hf_to_gguf.py --help,llama-quantize --helpdlstreamergst-inspect-1.0per elementnpu-umdovmsllama-vulkanrun.shnever ran. The too-big path stays, covered bytests/canary_test.sh.Mutation checks
llama-cpu's pin atserver-b99999@sha256:0…01and targeted this branch, so only Canary ran.llama-cpuand went red:pull failed after 3 attempts: ... not found. The report failed.--served-model-namein the vLLM fixture turnstests/canary_surface_test.pyred (6 of 35 fail; rerun on 5be20ca).surface.py.tests/canary_test.shfails exactly the two hosted false reds: sglang--tpand llm-scaler 0.26.0.Tests
tests/canary_test.shtests/canary_spec_test.shtests/canary_surface_test.pyThe three suites are wired into
ci.yml(jobcanary-tests).canary_test.shrunscanary/run.shagainsttests/openai_stub.pyandtests/fake_ovms.pybehind a docker shim. It covers:[DONE], a version mismatch, and a golden mismatch (pass, hard true, exact false);avx512f, too-big, pull retries, an informational failure, and model fetch then cache;run.sh --surfacefor sglang with the image's real help and_version.py: a pass through the--tpabbreviation, and a fail once a second option begins with--tp;0.26.1.dev0+g568afb3a1.d20260907.xpu, which passes, and at.dev4, which fails namingconsole/plan.py.canary_spec_test.shcovers:flags.shfailure on an unattributed literal;node.sh'sNODE_ARGSare ones the launcher passes.canary_surface_test.pyruns the parser on real inputs:arg_utils.py,envs.pyandlaunchers/cli_args.py;A present flag passes, a removed flag fails,
VLLM_USE_V1is absent and fails, and broken trees are unparseable. For argparse abbreviations, a unique prefix passes; an ambiguous one, a prefix of no option and a new option that makes it ambiguous all fail, and vLLM takes exact names only. The setuptools-scm cases are a patched tag build, a build after a tag, and no scm node.Other suites, all green.
tests/claims_check.sh,tests/ledger_drift_check.pyandtests/ci_wiring_test.sh(72 passed).scripts/images-lock checkandpins,tests/image_pins_test.shandtests/packaging_test.sh.tests/bench_test.sh,tests/bench_vs_test.py,bench/admission/test_run.sh, andtests/tune_test.shwithTUNE_E2E_FULL=1.Lint. actionlint 1.7.12 (
hack/ci-images.env) and shellcheck v0.11.0 are clean.Facts verified at build time
Actions. Each is the latest release, resolved with
gh apion 2026-10-29:actions/checkoutactions/cacheactions/upload-artifactactions/download-artifactModels. Each pin is the HF HEAD, checked again on 2026-10-29:
Qwen/Qwen3-0.6BOpenVINO/Qwen3-0.6B-int8-ovggml-org/Qwen3-0.6B-GGUFQ8_0)k3d v5.9.0.
k3d cluster create --helplists--wait(default true) and--timeout, and the binary's sha256 matchespackaging/install.sh.vLLM v0.32.0 (commit 5df63a72).
vllm/entrypoints/launchers/cli_args.py, so the parser globsentrypoints/**/cli_args.py.vllm._C_AVX2(vllm/platforms/cpu.py).vLLM v0.26.0 is commit 568afb3a13806beb53bb2e6bd518269357b237c0.
setuptools-scm. In
vcs_versioning/_version_schemes/_standard.py,guess_next_dev_versionreturns the tag only when the build isexact, meaning distance 0 and not dirty. Otherwise it returns the next version with.dev<distance>.sglang v0.5.22-xeon.
prepare_server_argsbuildsargparse.ArgumentParser(prog="sglang serve"), which allows abbreviations. The image runs Python 3.12.15.--tp 1parses totp_size1, and--t 1gives "ambiguous option".--tp 2.sglang/_version.py(0.5.22), because the distribution issglang-cpu.OVMS 2026.4.1.
GET /v3/models,GET /v2(version2026.4.1.cfc9ac721) and/v3/completionswithtop_kandseed.--pullkeeps an existing non-git model directory ("Skipping download and using existing files"). The run therefore setsHF_ENDPOINTto an unreachable address.NPU user space. The image's
intel-level-zero-npuis1.35.0.20260722-29947505341~ubuntu24.04, and the firmwarevpu_37xx_v1.binis built20260630. Both equalNPU_UMD_DRIVERandNPU_UMD_FW_DATE.Known limits
vllm serve --help=allraises "Failed to infer device type" without a device, so the check reads their flags from the installed sources. Their env names always come fromenvs.py.argparse.SUPPRESS) could make--tpambiguous to argparse but not to the canary.Owner actions
None are needed for this part. Until
canary.ymlis onmain, two things do not work, because GitHub requiresworkflow_dispatchworkflows to be on the default branch:engines=dispatch.Pull-request and scheduled runs are unaffected.