Found while fixing #1369, which edits run_drive — a shipped CLI subcommand behind
#[cfg(feature = "cpal-backend")].
The gap, measured rather than argued
Every routine check builds with --no-default-features, which switches cpal-backend off:
scripts/gate.sh → cargo clippy --workspace --no-default-features --all-targets and
cargo test --workspace --no-default-features
.cargo-husky/hooks/pre-push:73,84 → the same two flags
So #[cfg(feature = "cpal-backend")] code is never type-checked by either.
Probe, run at 331ba2d5. A planted type error inside run_drive
(engine.this_method_does_not_exist(); — valid syntax, no such method):
GATE clippy --workspace --no-default-features --all-targets -> rc=0 (invisible)
cargo check -p openpulse-cli --features cpal-backend -> rc=101 (caught)
error[E0599]: no method named `this_method_does_not_exist` found for struct `ModemEngine`
Scope it correctly — my first probe got this wrong. A syntax error IS caught (rc=101), because
#[cfg]-disabled code must still parse. What escapes is everything after parsing: type errors,
borrow errors, wrong arity, renamed methods. So the failure mode is not "garbage compiles" but
"the feature-gated path silently rots as the APIs under it change" — exactly the code most likely
to drift, since nobody compiles it while editing something else.
What is behind that gate
openpulse-cli's cpal-backend is on by default, so a developer running plain
cargo build -p openpulse-cli does compile it — which is why this has not bitten yet. It is the
automated checks that do not.
calibrate drive is the example at hand: it keys a real transmitter and is the operator's TX-drive
tuning tool. Its correctness matters more than most, and no automatic check compiles it.
CI does not close it either
ci.yml:324 has a Build cpal CLI step, but it is (a) inside jobs scoped to
startsWith(github.head_ref, 'release/') || workflow_dispatch (#1120/#1144), and (b) conditional on
steps.aloop.outputs.available == 'true' — snd-aloop being present on the runner, with a
::warning:: and a skip when it is not. So on a normal PR it never runs, and on a release it runs
only if the runner cooperates.
Options
- Add one
cargo check --features cpal-backend step to gate.sh. Cheap — a check, not a test
run, and only the crates that have the feature. It would have caught the planted error. Note
openpulse-audio, openpulse-ardop, openpulse-kiss and openpulse-testbench have their own
cpal features with the same exposure, so the step should cover the set rather than the CLI alone.
- Make the pre-push hook do the same for touched crates that carry a cpal feature.
- Accept it, and record in
CLAUDE.md that feature-gated paths are developer-compiled only — the
honest version of the status quo.
Related: #1144 (the gate does not run at merge), #1120 (why ci.yml is release-scoped). Distinct
from both: this is code that no configuration of the local gate compiles.
🤖 Generated with Claude Code
https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6
Found while fixing #1369, which edits
run_drive— a shipped CLI subcommand behind#[cfg(feature = "cpal-backend")].The gap, measured rather than argued
Every routine check builds with
--no-default-features, which switchescpal-backendoff:scripts/gate.sh→cargo clippy --workspace --no-default-features --all-targetsandcargo test --workspace --no-default-features.cargo-husky/hooks/pre-push:73,84→ the same two flagsSo
#[cfg(feature = "cpal-backend")]code is never type-checked by either.Probe, run at
331ba2d5. A planted type error insiderun_drive(
engine.this_method_does_not_exist();— valid syntax, no such method):Scope it correctly — my first probe got this wrong. A syntax error IS caught (rc=101), because
#[cfg]-disabled code must still parse. What escapes is everything after parsing: type errors,borrow errors, wrong arity, renamed methods. So the failure mode is not "garbage compiles" but
"the feature-gated path silently rots as the APIs under it change" — exactly the code most likely
to drift, since nobody compiles it while editing something else.
What is behind that gate
openpulse-cli'scpal-backendis on by default, so a developer running plaincargo build -p openpulse-clidoes compile it — which is why this has not bitten yet. It is theautomated checks that do not.
calibrate driveis the example at hand: it keys a real transmitter and is the operator's TX-drivetuning tool. Its correctness matters more than most, and no automatic check compiles it.
CI does not close it either
ci.yml:324has aBuild cpal CLIstep, but it is (a) inside jobs scoped tostartsWith(github.head_ref, 'release/') || workflow_dispatch(#1120/#1144), and (b) conditional onsteps.aloop.outputs.available == 'true'— snd-aloop being present on the runner, with a::warning::and a skip when it is not. So on a normal PR it never runs, and on a release it runsonly if the runner cooperates.
Options
cargo check --features cpal-backendstep togate.sh. Cheap — a check, not a testrun, and only the crates that have the feature. It would have caught the planted error. Note
openpulse-audio,openpulse-ardop,openpulse-kissandopenpulse-testbenchhave their owncpalfeatures with the same exposure, so the step should cover the set rather than the CLI alone.CLAUDE.mdthat feature-gated paths are developer-compiled only — thehonest version of the status quo.
Related: #1144 (the gate does not run at merge), #1120 (why
ci.ymlis release-scoped). Distinctfrom both: this is code that no configuration of the local gate compiles.
🤖 Generated with Claude Code
https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6