mirror of
https://github.com/headroomlabs-ai/headroom.git
synced 2026-08-27 14:17:10 -04:00
Five gates broke on the 2026-04-27 push of the smart_crusher branch.
Each is fixed below; the second half adds a `make ci-precheck` target
(plus an installable git pre-push hook) so the same dance never happens
again.
Failures fixed:
1. cargo fmt — 22 files had formatting drift introduced over the
stage 3c.1 work. `cargo fmt --all` reformatted them; no semantic
changes. `cargo test --workspace` still green (388 + supporting).
2. wheels job (macOS x86_64) — `fastembed -> ort -> ort-sys` does not
publish prebuilt ONNX Runtime binaries for `x86_64-apple-darwin`.
Removed that target from `.github/workflows/rust.yml`'s wheels
matrix. Apple Silicon (`aarch64-apple-darwin`) covers macOS
distribution; Intel macOS users can build from source. The matrix
now has 2 targets: linux x86_64 + macOS aarch64.
3. test-extras (relevance.py) — `tests/test_relevance.py::TestSmartCrusherIntegration`
constructs a `SmartCrusher`, which hard-imports `headroom._core`
since the python implementation was retired in stage 3c.1b. The
test-extras job didn't build the rust extension. Added the same
`maturin build + symlink` block the main `test` job uses.
4. smoke-test (eval.yml) — same root cause:
`compression_only.evaluate_ccr_lossless` instantiates a SmartCrusher.
Same fix: build the rust extension before the smoke test runs.
5. commitlint — three rules tripped:
- `subject-case` rejects PascalCase identifiers in subjects, but
the project deliberately names classes (SmartCrusher, HfTokenizer,
ContentRouter, DiffCompressor) in commit subjects. Disabled.
- `footer-leading-blank` is a warning that the wagoid action turns
into a CI failure; lines like `Module: foo.rs` in our bodies
match the conventional footer pattern and trip it. Disabled.
- `type-enum` doesn't include `parity`, but the project ships
parity-test infrastructure as its own concern (separate from
`test:`); added `parity` to the allowed types.
Pre-push verification — the prevention half:
`make ci-precheck` runs all of the above CI gates locally:
- `ci-precheck-rust`: cargo fmt --check + clippy + test --workspace.
- `ci-precheck-python`: builds the rust extension via maturin, then
runs the smart_crusher-affected python test files (185 tests across
test_transforms/, test_relevance*, test_ccr, test_acceptance,
test_critical_fixes, test_quality_retention).
- `ci-precheck-commitlint`: `npx commitlint --from origin/main --to
HEAD` against the same config CI uses. Skipped silently if npx is
not on PATH (install Node 18+ to enable).
`make install-git-hooks` (or `scripts/install-git-hooks.sh`) installs
a git pre-push hook that runs `make ci-precheck` automatically.
Bypass with `--no-verify` only when truly needed.
When new CI gates land in `.github/workflows/`, mirror them into a
`make ci-precheck-*` target. The Makefile is the local mirror of the
CI configuration; keeping them in sync is a load-bearing invariant.
Verification: `make ci-precheck` runs green on this commit.
66 lines
2.2 KiB
Rust
66 lines
2.2 KiB
Rust
//! Diagnostic: load one parity fixture and print expected vs actual.
|
|
//!
|
|
//! Usage: cargo run -p headroom-parity --example diff_fixture -- <path-to-fixture.json>
|
|
|
|
use anyhow::{bail, Context, Result};
|
|
use headroom_parity::{builtin_comparators, Fixture};
|
|
use std::env;
|
|
use std::fs;
|
|
|
|
fn main() -> Result<()> {
|
|
let path = env::args()
|
|
.nth(1)
|
|
.context("usage: diff_fixture <fixture.json>")?;
|
|
let bytes = fs::read(&path).context("reading fixture")?;
|
|
let fixture: Fixture = serde_json::from_slice(&bytes).context("parsing fixture")?;
|
|
|
|
let comparator = builtin_comparators()
|
|
.into_iter()
|
|
.find(|c| c.name() == fixture.transform)
|
|
.with_context(|| format!("no comparator named {}", fixture.transform))?;
|
|
|
|
let actual = match comparator.run(&fixture.input, &fixture.config) {
|
|
Ok(v) => v,
|
|
Err(e) => bail!("comparator failed: {e}"),
|
|
};
|
|
|
|
let expected_pretty = serde_json::to_string_pretty(&fixture.output)?;
|
|
let actual_pretty = serde_json::to_string_pretty(&actual)?;
|
|
|
|
println!("=== Expected (Python) ===");
|
|
println!("{expected_pretty}");
|
|
println!("\n=== Actual (Rust) ===");
|
|
println!("{actual_pretty}");
|
|
|
|
if actual == fixture.output {
|
|
println!("\n=== MATCH ===");
|
|
} else {
|
|
println!("\n=== DIFFER ===");
|
|
// Field-by-field for objects
|
|
if let (Some(exp_obj), Some(act_obj)) = (fixture.output.as_object(), actual.as_object()) {
|
|
for key in exp_obj
|
|
.keys()
|
|
.chain(act_obj.keys())
|
|
.collect::<std::collections::BTreeSet<_>>()
|
|
{
|
|
let e = exp_obj.get(key);
|
|
let a = act_obj.get(key);
|
|
if e != a {
|
|
println!(" field {key}:");
|
|
println!(
|
|
" expected: {}",
|
|
e.map(|v| serde_json::to_string(v).unwrap())
|
|
.unwrap_or_default()
|
|
);
|
|
println!(
|
|
" actual : {}",
|
|
a.map(|v| serde_json::to_string(v).unwrap())
|
|
.unwrap_or_default()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|