Use this pipeline only to maintain or retire an existing fixture. It predates structured local qualification and keeps a compact result tied to a separately retained raw log. New correctness, performance, capacity, and endurance evidence belongs in
scripts/qualification/; see Local hardware qualification.
A legacy latency artifact answers one narrow question: did a named microbenchmark, on its named hardware and source, satisfy its own reviewed threshold? It is not a qualification receipt, a cross-device benchmark, a backend support gate, or a product default.
Current Legacy Gate State
docs/backend-latency-fixtures.json currently reports
status: "fixture_required".
| Backend | Fixture state | Meaning |
|---|---|---|
| CUDA | One locked RTX 4090 matmul fixture | Its own reviewed metrics have numeric thresholds |
| ROCm | One locked gfx1151 matmul fixture | Its own reviewed metrics have numeric thresholds |
| Metal | Two Apple Silicon fixtures pending | Result files exist, but reviewed thresholds are not locked |
| Vulkan | One Strix Halo decode fixture pending | This machine-specific regression fixture is incomplete |
The Vulkan fixture’s device name and self-hosted runner label identify where that old measurement must run. They do not restrict Kiln’s Vulkan runtime to Strix Halo, select a Vulkan device in product code, or define support for other Vulkan-capable devices.
Check the non-strict manifest state with:
python3 scripts/check_backend_latency_fixtures.py \
docs/backend-latency-fixtures.json
The strict command fails until every required fixture has a reviewed artifact and locked thresholds:
python3 scripts/check_backend_latency_fixtures.py \
docs/backend-latency-fixtures.json \
--require-covered
State Machine
| Manifest state | Required fixture fields | What CI may claim |
|---|---|---|
fixture_required |
At least one fixture still uses pending_fixture_result, with metric max: null |
Default checks may validate the manifest, but may not claim the legacy gate is covered |
covered |
Every fixture uses locked_threshold, every metric has a finite numeric threshold, and every result passes strict validation |
The legacy fixture set is internally covered |
For a pending fixture:
threshold_stateispending_fixture_result;- each metric
maxisnull; and - the manifest remains
fixture_required.
For a covered fixture:
threshold_stateislocked_threshold;- each metric has a reviewed finite threshold;
result_artifactnames a tracked JSON file belowbench-results/backend-latency; and- the ignored or externally retained raw log is bound by digest.
“Covered” means this legacy fixture contract is complete. It does not mean the backend is fast on every device, or even that an end-to-end model server met an SLO.
Result Artifact
Artifact schema version 3 is a closed JSON object:
{
"artifact_schema_version": 3,
"created_at_utc": "2026-06-06T12:00:00Z",
"fixture_id": "metal_apple_silicon_matmul_qwen35_4b",
"backend": "metal",
"status": "passed",
"manifest": "docs/backend-latency-fixtures.json",
"manifest_schema_version": 1,
"fixture_spec_sha256": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"hardware": "Apple Silicon Metal fixture",
"source": "crates/kiln-tensor/tests/metal_matmul_bench.rs",
"source_sha256": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
"command": "cargo test -p kiln-tensor --features metal --test metal_matmul_bench -- --ignored --nocapture",
"raw_log": "bench-results/backend-latency/raw/metal-apple-silicon-matmul-qwen35-4b-20260606T120000Z.log",
"raw_log_sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"git_commit": "1111111111111111111111111111111111111111",
"git_tracked_dirty": false,
"metrics": {
"decode_qkv_m1_2560x4096_ms": 1.23
}
}
| Field | Meaning and validation |
|---|---|
artifact_schema_version |
Must be 3 |
created_at_utc |
UTC ISO-8601 creation time ending in Z |
fixture_id, backend, hardware, source, command |
Must exactly match the selected manifest fixture |
manifest |
Non-empty repository-relative manifest path; checked against the validator input when available |
manifest_schema_version |
Must match the manifest’s schema version |
fixture_spec_sha256 |
SHA-256 of the stable fixture definition |
source_sha256 |
SHA-256 of the fixture source bytes |
raw_log, raw_log_sha256 |
Repository-relative retention reference and digest for the captured log |
git_commit |
Lowercase 40-hex commit that must exist locally during strict validation |
git_tracked_dirty |
Historical field name for a clean-checkout marker; false is required for coverage |
status |
Must be passed |
metrics |
Exactly the declared metric names, each with a finite numeric value |
The writer computes git_tracked_dirty from
git status --porcelain --untracked-files=all. Despite its name, tracked
changes and new untracked repository files both make the artifact dirty.
Unknown top-level keys and undeclared metrics are rejected. Additive artifact changes require a schema-version bump and validator update.
Fixture Identity
fixture_spec_sha256 covers:
- fixture ID and backend;
- hardware label;
- source path;
- command;
- metric name, unit, and comparison; and
- selected cases, when present.
It deliberately excludes metric thresholds, threshold_state, and
result_artifact. This lets reviewers ingest a measurement first and lock
headroom afterward. Changing the command, hardware label, source, source
bytes, metric identity, unit, comparison, or selected cases requires a new
hardware artifact.
Fixture command strings are part of this digest. Keep them runner-portable:
use cargo ..., declared environment variables, or explicit fixture arguments.
Do not store a developer-local Cargo path or a placeholder model path.
Metric Boundary
Benchmarks emit machine-readable lines alongside normal output:
KILN_LATENCY_METRIC <metric> <value> <unit>
The writer extracts only metrics declared by the selected fixture. Import and threshold locking reparse the raw log and require every retained value and unit to match.
Each finite observed value is tested against its own fixture threshold:
comparison: "<="means the value must be at mostmax; andcomparison: ">="means the value must be at leastmax.
Valid conclusions stay within one fixture definition. Do not compare two artifacts as interchangeable when their hardware, command, source, selected cases, metric definition, or fixture digest differs. These microbenchmark numbers are not end-to-end tokens per second or request latency.
Trust Boundary
Strict validation checks that the manifest, compact artifact, source, commit, raw-log digest, parsed metrics, and thresholds agree. It does not prove that:
- the named hardware was honestly reported;
- the host or artifact producer was authenticated;
- an external or missing raw log still exists;
- the artifact is signed;
- another device will reproduce the measurement; or
- a model server will achieve corresponding throughput or latency.
At import and threshold-lock time, the raw log must exist below
bench-results/backend-latency/raw, remain ignored, match its SHA-256 digest,
and reparse to the retained metrics. After locking, the raw log may be absent
from a clean checkout if it remains in external workflow storage. When a local
copy is present, strict validation checks it again.
The compact result must be tracked under bench-results/backend-latency with a
.json extension. The fixture source must match both the working tree and the
recorded commit. The receipt has no independent signature or chain of custody.
Maintain One Existing Fixture
Run the selected command, capture its log, and write the artifact in one step:
python3 scripts/run_backend_latency_fixture.py \
docs/backend-latency-fixtures.json \
metal_apple_silicon_matmul_qwen35_4b
To materialize from an already captured log:
python3 scripts/write_backend_latency_result_artifact.py \
docs/backend-latency-fixtures.json \
metal_apple_silicon_matmul_qwen35_4b \
/absolute/path/to/raw-benchmark.log
By default, the writer uses the fixture’s result_artifact. Use --output for
a scratch artifact.
If a manual GitHub Actions run produced a downloadable artifact, import its zip or extracted directory:
python3 scripts/import_backend_latency_artifact.py \
/absolute/path/to/downloaded-artifact.zip \
--fixture-id metal_apple_silicon_matmul_qwen35_4b
The importer validates record shape, fixture and source identity, raw-log
digest, parsed values, commit identity, and the clean-checkout marker. It does
not require locked thresholds. If canonical files already contain different
bytes, use --force only after review has chosen the replacement run.
Commit the compact result and reviewed manifest change. Do not force-add the ignored raw log.
Manual Workflow Compatibility
The manual-only Perf regression nightly workflow is an unscheduled
compatibility handoff. Pull requests do not run it, and its result is not
structured qualification evidence.
List the remaining fixture work:
python3 scripts/plan_backend_latency_fixture_dispatch.py
The JSON plan reports whether site-local self-hosted runner labels are missing
and supplies dispatch, download, import, lock, and strict-check commands.
runner_labels is CI routing metadata; it is excluded from the fixture digest
and has no product-runtime meaning.
Use --check-runners to query GitHub runner availability through gh api.
Use --shell only when every selected fixture has labels:
python3 scripts/plan_backend_latency_fixture_dispatch.py \
--fixture-id cuda_rtx4090_matmul_qwen35_4b \
--shell
Lock Reviewed Thresholds
After reviewing the artifact and raw log, add explicit headroom:
python3 scripts/lock_backend_latency_thresholds.py \
docs/backend-latency-fixtures.json \
--headroom 0.10 \
--fixture-id metal_apple_silicon_matmul_qwen35_4b
The locker:
- validates the manifest, result, source, commit, clean-checkout marker, raw log, parsed metrics, and units;
- raises
<=limits above the observed value; - lowers
>=floors below the observed value; - leaves unselected fixtures pending; and
- changes the manifest to
coveredonly when every fixture is locked.
Use --check to preview validation without writing. Then run the strict gate:
python3 scripts/check_backend_latency_fixtures.py \
docs/backend-latency-fixtures.json \
--require-covered
Failure Triage
| Failure | Inspect |
|---|---|
| Missing metric | Required KILN_LATENCY_METRIC line, spelling, and unit |
| Fixture digest mismatch | Command, hardware label, source, metrics, or selected cases changed |
| Source digest mismatch | Working-tree source differs from the captured or committed source |
| Dirty marker rejected | Tracked changes or untracked repository files existed at capture |
| Raw-log mismatch | Wrong log, changed bytes, or retained metrics differ from reparsing |
| Commit rejected | Commit is malformed, unavailable locally, or lacks the fixture source |
| Threshold failure | Observed value violates its own comparison and locked limit |
| Strict gate remains incomplete | At least one required backend fixture is still pending |
Do not resolve a fixture failure by copying its machine identity into portable backend code. Fix the implementation, recapture the same regression fixture, or replace the legacy fixture through review.
Tooling Self-Tests
These checks exercise parsing and workflow mechanics without hardware:
python3 scripts/run_backend_latency_fixture.py --self-test
python3 scripts/write_backend_latency_result_artifact.py --self-test
python3 scripts/import_backend_latency_artifact.py --self-test
python3 scripts/lock_backend_latency_thresholds.py --self-test
python3 scripts/check_backend_latency_fixtures.py --self-test
python3 scripts/plan_backend_latency_fixture_dispatch.py --self-test