diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f4adc5f9..7c1ea902 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -55,6 +55,37 @@ jobs:
# streaming.
run: cargo build -p wickra-examples --bins
+ # Clippy for the Python and Node bindings. These are kept out of the main
+ # `rust` job because PyO3 / napi build scripts need a Python interpreter and
+ # a Node toolchain on PATH, which the 3-OS matrix job does not provision.
+ # Ubuntu-only is sufficient: the lints are platform-independent.
+ clippy-bindings:
+ name: Clippy bindings
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+
+ - name: Install Rust toolchain
+ uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
+ with:
+ components: clippy
+
+ - name: Set up Python
+ uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ with:
+ python-version: "3.12"
+
+ - name: Set up Node
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: "20"
+
+ - name: Cache cargo
+ uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
+
+ - name: Clippy (bindings, all targets)
+ run: cargo clippy -p wickra-node -p wickra-python --all-targets -- -D warnings
+
# Verify the crates still build and test on their declared minimum supported
# Rust version. The workspace pins rust-version = "1.86" — that floor is
# set by criterion 0.8.2 (the bench dev-dep), which itself rolled past the
diff --git a/bindings/node/src/lib.rs b/bindings/node/src/lib.rs
index 4ebc86f6..65106837 100644
--- a/bindings/node/src/lib.rs
+++ b/bindings/node/src/lib.rs
@@ -7310,6 +7310,12 @@ pub struct TdRangeProjectionNode {
inner: wc::TdRangeProjection,
}
+impl Default for TdRangeProjectionNode {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[napi]
impl TdRangeProjectionNode {
#[napi(constructor)]
@@ -7379,6 +7385,12 @@ pub struct TdDifferentialNode {
inner: wc::TdDifferential,
}
+impl Default for TdDifferentialNode {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[napi]
impl TdDifferentialNode {
#[napi(constructor)]
@@ -7434,6 +7446,12 @@ pub struct TdOpenNode {
inner: wc::TdOpen,
}
+impl Default for TdOpenNode {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[napi]
impl TdOpenNode {
#[napi(constructor)]
@@ -7712,6 +7730,12 @@ pub struct HilbertDominantCycleNode {
inner: wc::HilbertDominantCycle,
}
+impl Default for HilbertDominantCycleNode {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[napi]
impl HilbertDominantCycleNode {
#[napi(constructor)]
@@ -7747,6 +7771,12 @@ pub struct AdaptiveCycleNode {
inner: wc::AdaptiveCycle,
}
+impl Default for AdaptiveCycleNode {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[napi]
impl AdaptiveCycleNode {
#[napi(constructor)]
@@ -7782,6 +7812,12 @@ pub struct SineWaveNode {
inner: wc::SineWave,
}
+impl Default for SineWaveNode {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[napi]
impl SineWaveNode {
#[napi(constructor)]
@@ -8138,7 +8174,7 @@ impl ValueAreaNode {
low: f64,
volume: f64,
) -> napi::Result