Skip to content

Review & the gauntlet

You’ve written a change. Now you have to land it. A change that breaks the build or slips an unreviewed decision past a reviewer doesn’t land. This lesson is the gate every change passes through: the CI gauntlet, the source-level guards that keep retired code gone, and the checklist a contributor runs on their own diff before asking anyone to look. This is a run-and-read lesson: you’ll run the real gauntlet commands and predict which gate catches which kind of mistake.

  • Run the local mirror of CI (fmt, both clippy passes, the test suite) before you push.
  • Explain why clippy runs twice on purpose and what the no---all-targets pass protects.
  • Name what a retire-gate is, what it guards, and the design rule behind it.
  • Predict which CI gate fails for a given mistake (a format slip, a dead pub(crate) item, a revived retired identifier).
  • Apply the engine’s review checklist to your own diff: the scope, hidden-change, and weakened-test items CI can’t catch.

New terms in this lesson (each is also expanded inline at the point you first need it):

  • the gauntlet: the whole CI run a change must survive.
  • CI gate: one pass/fail check that can block a merge.
  • retire-gate: a test that keeps deleted code from creeping back.
  • review checklist: what a contributor runs on their own diff before asking for review.

CI is defined in one file. The gauntlet runs five jobs in parallel: a check job on Linux, the full suite on Windows and macOS (three first-class targets), a cross-compile type-check, and a dependency-license audit. Only the check job has an internal order; the rest are single steps.

clinker ·ci.yml doc @19acdcb4
jobs:
check: # ubuntu-latest, toolchain "1.91"
steps:
- run: cargo fmt --all --check
- run: cargo clippy --workspace -- -D warnings
- run: cargo clippy --workspace --all-targets -- -D warnings
- run: cargo test --workspace
- run: cargo check --benches --workspace
- run: cargo check --features bench-alloc -p clinker-benchmarks
- run: cargo test --benches -p clinker-benchmarks
test-windows: # windows-latest → cargo test --workspace
test-macos: # macos-latest → cargo test --workspace
cross-platform:# ubuntu → cargo check --target {windows,apple} for the no-C crates
deny: # EmbarkStudios/cargo-deny-action@v2

Each step is one CI gate. The order inside check is the order to internalize, because it’s the cheapest-failure-first order: formatting (instant) → lint → test → bench gates. Windows and macOS run the full suite on native runners specifically because ring (TLS) needs a native C toolchain that the Linux cross-check can’t link, so the platform #[cfg(windows)] / #[cfg(macos)] tests genuinely execute on real runners, not just type-check.

The two clippy lines aren’t a copy-paste mistake. The CI file comments the reason verbatim, and it’s a clever interaction worth understanding:

clinker ·50_TESTING_AND_COMMANDS.md ·CI intentionally runs both clippy passes: doc @19acdcb4
# Two clippy passes. The first omits --all-targets on purpose: with
# test targets excluded, a pub(crate) item referenced only from
# #[cfg(test)] code still trips the dead-code lint, so the dead-code
# gate keeps working. The second adds --all-targets to lint test,
# bench, and example code that the first pass never compiles.
- run: cargo clippy --workspace -- -D warnings
- run: cargo clippy --workspace --all-targets -- -D warnings

Read it slowly. If clippy only ran with --all-targets, then a pub(crate) function used only from tests would look “used,” and dead production code that happens to have a test would never be flagged. By running once without test targets, the first pass sees that function as dead (nothing in the library uses it) and fails. The second pass then lints the test/bench/example code the first pass skipped. The design rule states it plainly:

clinker ·30_DESIGN_RULES.md ·Keep dead-code pressure intact. doc @19acdcb4

Keep dead-code pressure intact. CI intentionally runs cargo clippy --workspace -- -D warnings without --all-targets before the all-targets pass so test-only pub(crate) items still fail as dead code.

Use → Modify → Create: drive the gauntlet

Section titled “Use → Modify → Create: drive the gauntlet”

You learn the gauntlet by running it, then perturbing it, then predicting it from scratch. Each rung adds exactly one new demand.

The local mirror of the whole gauntlet reproduces the check job’s order on your machine. Run it before you push, and CI rarely surprises you:

Terminal window
cargo fmt --all --check
cargo clippy --workspace --locked --offline -- -D warnings
cargo clippy --workspace --all-targets --locked --offline -- -D warnings
ulimit -n 4096 && cargo test --workspace --locked --offline

The ulimit -n 4096 prefix is load-bearing on the test step (the default 1024-fd limit makes clinker-exec’s spill tests fail with “Too many open files,” the same prefix you met in the testing strategy lesson). A clean run prints, in order: no fmt diff, two clippy passes with 0 warnings, then the test result: ok summary. That clean transcript is the “all gates green” state every merge needs.

Modify: introduce one fault, predict the gate

Section titled “Modify: introduce one fault, predict the gate”

Now perturb the codebase one fault at a time and predict which gate catches it before you re-run. Here are two perturbations grounded in the commands above:

Terminal window
# Perturbation A — drop a blank line or mis-indent a line in any committed .rs file.
# Predict: which of the four steps fails, and is it before or after the tests run?
# Perturbation B — add this to a library module (NOT a test module):
# pub(crate) fn only_used_by_a_test() -> i64 { 7 }
# ...and call it solely from a #[cfg(test)] test in the same file.
# Predict: does the FIRST clippy pass pass or fail? Does the SECOND?
💡 Hint 1

Perturbation A trips the very first step (cargo fmt --all --check). Fmt is the cheapest gate, so it fails before clippy or the tests run at all. Perturbation B is the dead-code case: the first, no---all-targets clippy pass sees the helper as unused and fails under -D warnings; the --all-targets pass compiles the test, sees the call, and would pass.

Show solution

A → cargo fmt --all --check, the first step, failing before tests. B → the first clippy pass fails, the second would pass. That is the entire reason both passes exist. Revert each with git restore once you’ve watched the gate fire. The lesson of B: a fault can be visible to a narrower check and invisible to a broader one, so the gauntlet runs both deliberately.

Create: a retire-gate, and predict it firing

Section titled “Create: a retire-gate, and predict it firing”

When the project retires something (an old module name, a withdrawn diagnostic code), deleting it isn’t enough; someone could reintroduce it later. Clinker installs retire-gates: tiny source-text tests that fail CI if a retired identifier reappears.

clinker-plan ·lib.rs ·rename_gates test @19acdcb4
mod rename_gates {
//! Source-text gates asserting retired identifiers stay gone: the old
//! `cxl_compile` module name and the retired `E100` diagnostic code
//! must not reappear in the config, error, or plan sources.
#[test]
fn e100_diagnostic_code_is_absent() {
for (name, src) in [
("config/mod.rs", include_str!("config/mod.rs")),
("error.rs", include_str!("error.rs")),
] {
assert!(
!src.contains("\"E100\""),
"found \"E100\" string literal in {name} — E100 should be fully retired"
);
}
}
}

This is a pattern worth recognizing: a test whose subject is the source text itself, via include_str!. It encodes a decision (“E100 is gone, don’t bring it back”) as an executable guard. The design rule it enforces: “Do not add compatibility shims around retired config shapes or old module names.” If your change needs to revive a retired name, that’s a decision to escalate, not a code change to make alone (the subject of the next lesson).

Your create-and-predict task: without running anything yet, predict the exact behavior of this one-line perturbation, then verify it.

Terminal window
# Add the literal string "E100" back into crates/clinker-plan/src/error.rs,
# then run: cargo test -p clinker-plan --offline rename_gates
# Predict — three parts:
# 1. Does this fail the fmt gate, a clippy gate, or the test gate?
# 2. What does the panic MESSAGE name?
# 3. Is reviving E100 a code fix, or a decision to escalate?
Show solution
  1. The test gate. rename_gates is an ordinary #[test]; fmt and clippy don’t read string contents for retired identifiers, so neither would flag it. Only the source-text assertion does.
  2. The panic prints found "E100" string literal in error.rs — E100 should be fully retired, naming the file and the retired identifier so you know exactly which guard you tripped.
  3. It’s a decision to escalate. Reviving a retired name contradicts a recorded design rule; the right move is to raise it, not to quietly delete the gate. Revert with git restore.

The review checklist, applied to your own diff

Section titled “The review checklist, applied to your own diff”

Before a change is reviewed by anyone else, the contributor runs the project’s review checklist on their own diff. The point is to catch the things CI can’t: scope creep, hidden decisions, weakened tests.

clinker ·GITHUB_ISSUE_AGENT_WORKFLOW.md ·Review doc @19acdcb4

The load-bearing items the contributor self-checks:

  • PR maps to exactly one issue or one coherent sub-issue. No unrelated cleanup.
  • No hidden public API, schema, dependency, auth, security, memory, or architecture change.
  • Tests were not weakened to pass. (The mirror of the testing-strategy lesson: a green suite earned by deleting an assertion is worse than a red one.)
  • Regression tests exist for bug fixes.
  • Follow-up work is captured as issues, not hidden TODO drift.
  • Verification commands were run or skipped with a reason.

Notice how much of this is about what didn’t happen: no hidden schema change, no weakened test, no silent scope expansion. That’s the reviewer’s real job, and the contributor’s first. CI proves the build is green; the checklist proves the diff is honest. A green gauntlet on a diff that quietly deleted an assertion is the most dangerous kind of green.

// quick check

Why does CI run `cargo clippy --workspace -- -D warnings` (no --all-targets) before the --all-targets pass?

You can build a change and land it. The final lesson steps back: how to plan a change so that it respects the engine’s architectural boundaries, and how to recognize a decision you must escalate rather than make alone.