From 37d07c1e80ff78aadab4b5e6b948e3439670c129 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 08:26:20 +0200 Subject: [PATCH 01/10] test: assert something in the tests that asserted nothing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nine assertions compared an unsigned counter against zero — `peer_count >= 0`, `uptime_secs >= 0`, `verification_time_ms >= 0`. Every one of them held for every possible value, including the failure each test exists to catch: `test_add_peer` passed whether or not the peer was added. Replace them with the claim the test is named for where there is one (peer counts), an invariant that can actually break where there is not (time in a power profile cannot exceed total uptime; energy is never negative), and nothing at all where the author's own comment says the value is legitimately zero — with a line explaining why no bound is asserted, so it does not get "fixed" back. Also: `3.14` as an arbitrary test float in eight places, which clippy reads as an approximation of PI. These tests are about storing a decimal, not about geometry, so use a number that does not claim to be a constant. And `aingle_zk`'s memory benchmark had not compiled since `prove_knowledge` settled on `(secret, public_point, message)` — it was still passing the last two swapped. Invisible to `cargo test`, which does not build benches. --- crates/aingle_graph/src/value.rs | 4 ++-- .../tests/graph_integration_tests.rs | 2 +- crates/aingle_minimal/src/node.rs | 18 +++++++++++------- crates/aingle_minimal/src/power.rs | 8 +++++--- .../tests/iot_integration_tests.rs | 13 ++++++++----- crates/aingle_zk/benches/full_benchmarks.rs | 10 +++++++--- crates/aingle_zk/src/aggregation.rs | 5 +++-- crates/kaneru/src/types.rs | 10 +++++----- 8 files changed, 42 insertions(+), 28 deletions(-) diff --git a/crates/aingle_graph/src/value.rs b/crates/aingle_graph/src/value.rs index dfa8836f..d948c28b 100644 --- a/crates/aingle_graph/src/value.rs +++ b/crates/aingle_graph/src/value.rs @@ -481,8 +481,8 @@ mod tests { let int_val = Value::integer(42); assert_eq!(int_val.as_integer(), Some(42)); - let float_val = Value::float(3.14); - assert_eq!(float_val.as_float(), Some(3.14)); + let float_val = Value::float(2.5); + assert_eq!(float_val.as_float(), Some(2.5)); } #[test] diff --git a/crates/aingle_graph/tests/graph_integration_tests.rs b/crates/aingle_graph/tests/graph_integration_tests.rs index 56860125..1029dda2 100644 --- a/crates/aingle_graph/tests/graph_integration_tests.rs +++ b/crates/aingle_graph/tests/graph_integration_tests.rs @@ -581,7 +581,7 @@ fn test_different_value_types() { db.insert(Triple::new( NodeId::named("node:1"), Predicate::named("float_val"), - Value::float(3.14), + Value::float(2.5), )) .unwrap(); diff --git a/crates/aingle_minimal/src/node.rs b/crates/aingle_minimal/src/node.rs index 6b9e4b18..3bef02a1 100644 --- a/crates/aingle_minimal/src/node.rs +++ b/crates/aingle_minimal/src/node.rs @@ -1037,9 +1037,11 @@ mod tests { let addr: std::net::SocketAddr = "192.168.1.100:5683".parse().unwrap(); node.add_peer(addr); - // Verify peer was added via stats + // Verify peer was added via stats. `peer_count` is unsigned, so the + // old `>= 0` here held for every possible value including "nothing was + // added" — the one outcome this test exists to rule out. let stats = node.stats().unwrap(); - assert!(stats.peer_count >= 0); // At least no error + assert_eq!(stats.peer_count, 1); } #[test] @@ -1052,9 +1054,9 @@ mod tests { node.add_peer("192.168.1.101:5683".parse().unwrap()); node.add_peer("192.168.1.102:5683".parse().unwrap()); - // Stats should work + // Stats should work, and report all three. let stats = node.stats().unwrap(); - assert!(stats.peer_count >= 0); + assert_eq!(stats.peer_count, 3); } #[test] @@ -1126,9 +1128,11 @@ mod tests { // Sleep a bit to ensure uptime is measurable std::thread::sleep(std::time::Duration::from_millis(10)); - let stats = node.stats().unwrap(); - // Uptime should be at least 0 (could be 0 if very fast) - assert!(stats.uptime_secs >= 0); + // Uptime is whole seconds, so a node this young legitimately reports 0 + // and there is no lower bound to assert: `>= 0` on a u64 was always + // true. What this test does establish is that `stats()` succeeds on a + // freshly started node, which is the `unwrap` below. + let _stats = node.stats().unwrap(); } #[test] diff --git a/crates/aingle_minimal/src/power.rs b/crates/aingle_minimal/src/power.rs index 8f0a231f..18280b38 100644 --- a/crates/aingle_minimal/src/power.rs +++ b/crates/aingle_minimal/src/power.rs @@ -590,9 +590,11 @@ mod tests { std::thread::sleep(Duration::from_millis(100)); let stats = pm.get_stats(); - // At least one metric should be non-zero - assert!(stats.uptime_secs >= 0); - assert!(stats.time_in_balanced >= 0); + // These counters are unsigned, so the old `>= 0` pair held always. Assert + // the invariant that can actually break instead: time spent in one + // profile cannot exceed the total uptime, and energy is never negative. + assert!(stats.time_in_balanced <= stats.uptime_secs); + assert!(stats.energy_consumed_mwh >= 0.0); } #[test] diff --git a/crates/aingle_minimal/tests/iot_integration_tests.rs b/crates/aingle_minimal/tests/iot_integration_tests.rs index 9bd70f68..c72532f8 100644 --- a/crates/aingle_minimal/tests/iot_integration_tests.rs +++ b/crates/aingle_minimal/tests/iot_integration_tests.rs @@ -260,10 +260,11 @@ fn test_power_stats_tracking() { pm.set_power_profile(PowerProfile::LowPower); let stats = pm.get_stats(); - // Stats should be valid (non-negative) - assert!(stats.uptime_secs >= 0); - assert!(stats.time_in_high_performance >= 0); - assert!(stats.time_in_low_power >= 0); + // These counters are unsigned, so "non-negative" was true by construction + // and asserted nothing. The real invariant: time attributed to the profiles + // this test drove cannot exceed the total uptime. + assert!(stats.time_in_high_performance <= stats.uptime_secs); + assert!(stats.time_in_low_power <= stats.uptime_secs); } #[test] @@ -400,7 +401,9 @@ fn test_complete_iot_device_workflow() { // 8. Get statistics let power_stats = power_manager.get_stats(); - assert!(power_stats.uptime_secs >= 0); + // `uptime_secs` is unsigned; the old `>= 0` asserted nothing. Energy is the + // figure this end-to-end run can actually get wrong. + assert!(power_stats.energy_consumed_mwh >= 0.0); let ota_stats = ota_manager.stats(); assert_eq!(ota_stats.updates_applied, 0); diff --git a/crates/aingle_zk/benches/full_benchmarks.rs b/crates/aingle_zk/benches/full_benchmarks.rs index d4b8c7a3..6a2fef2a 100644 --- a/crates/aingle_zk/benches/full_benchmarks.rs +++ b/crates/aingle_zk/benches/full_benchmarks.rs @@ -11,7 +11,7 @@ //! - Memory usage use aingle_zk::{ - aggregation::{aggregate_proofs, ProofAggregator}, + aggregation::aggregate_proofs, batch::{verify_schnorr_batch, BatchVerifier}, commitment::{HashCommitment, PedersenCommitment}, merkle::{MerkleTree, SparseMerkleTree}, @@ -442,12 +442,16 @@ fn benchmark_serialization(c: &mut Criterion) { /// Benchmark memory usage fn benchmark_memory(c: &mut Criterion) { - let mut group = c.benchmark_group("memory"); + // Not `mut`: this group records no measurements, it only prints sizes. + let group = c.benchmark_group("memory"); // Measure proof sizes let secret = Scalar::random(&mut OsRng); let public = RISTRETTO_BASEPOINT_POINT * secret; - let schnorr = SchnorrProof::prove_knowledge(&secret, b"test", &public); + // Argument order follows `prove_knowledge(secret, public_point, message)`. + // It was called with the last two swapped, so this benchmark had not + // compiled since that signature settled — invisible until `--all-targets`. + let schnorr = SchnorrProof::prove_knowledge(&secret, &public, b"test"); let schnorr_size = std::mem::size_of_val(&schnorr); println!("Schnorr proof in-memory size: {} bytes", schnorr_size); diff --git a/crates/aingle_zk/src/aggregation.rs b/crates/aingle_zk/src/aggregation.rs index 0976bb12..06aea982 100644 --- a/crates/aingle_zk/src/aggregation.rs +++ b/crates/aingle_zk/src/aggregation.rs @@ -506,8 +506,9 @@ mod tests { assert!(result.all_valid); assert_eq!(result.success_rate(), 1.0); assert!(result.avg_time_per_proof() >= 0.0); - // Verification time may be 0 if very fast (< 1ms) - assert!(result.verification_time_ms >= 0); + // No assertion on `verification_time_ms`: it is unsigned and legitimately + // 0 for a batch that verifies in under a millisecond, so every bound + // that could be written here is either always true or flaky. } #[test] diff --git a/crates/kaneru/src/types.rs b/crates/kaneru/src/types.rs index 34feffa2..9b84a614 100644 --- a/crates/kaneru/src/types.rs +++ b/crates/kaneru/src/types.rs @@ -293,8 +293,8 @@ mod tests { #[test] fn test_value_conversions() { - let v = Value::Float(3.14); - assert!((v.as_f64().unwrap() - 3.14).abs() < 0.001); + let v = Value::Float(2.5); + assert!((v.as_f64().unwrap() - 2.5).abs() < 0.001); let v = Value::Int(42); assert_eq!(v.as_i64().unwrap(), 42); @@ -372,7 +372,7 @@ mod tests { assert_eq!(Value::Bool(true).as_string(), "true"); assert_eq!(Value::Bool(false).as_string(), "false"); assert_eq!(Value::Int(42).as_string(), "42"); - assert_eq!(Value::Float(3.14).as_string(), "3.14"); + assert_eq!(Value::Float(2.5).as_string(), "2.5"); assert_eq!(Value::String("test".to_string()).as_string(), "test"); } @@ -393,9 +393,9 @@ mod tests { #[test] fn test_value_from_f64() { - let v: Value = 3.14f64.into(); + let v: Value = 2.5f64.into(); if let Value::Float(f) = v { - assert!((f - 3.14).abs() < 0.001); + assert!((f - 2.5).abs() < 0.001); } else { panic!("Expected Float"); } From df2c9dd0ba09bdcf53875441304c25937d882fd7 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 08:26:29 +0200 Subject: [PATCH 02/10] style: apply cargo clippy --fix across the workspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Machine-applicable suggestions only, applied by the tool and left otherwise untouched: needless borrows, redundant closures, `is_empty` over length comparisons, `io::Error::other`, derivable impls, unused imports. Mechanical and behaviour-preserving by construction, but it is 40 files of churn in the engine, so it is a separate commit: drop it without losing the fixes in the commit before, which are not mechanical. Twenty-four lints remain that clippy declines to auto-fix because they need judgement — clamp (NaN handling differs), `sort_by_key` where the key borrows, `Default::default()` field reassignment, and a public `from_str` that shadows the trait method and cannot be renamed without breaking callers. Left for a later pass rather than rushed. --- crates/aingle_ai/src/kaneru/mod.rs | 2 +- crates/aingle_ai/src/nested_learning/mod.rs | 4 ++-- .../src/nested_learning/transaction_level.rs | 2 +- crates/aingle_ai/src/types.rs | 2 +- crates/aingle_contracts/src/contract.rs | 7 ++----- crates/aingle_contracts/src/types.rs | 2 +- crates/aingle_cortex/src/cluster_init.rs | 11 +++++----- crates/aingle_cortex/src/p2p/config.rs | 5 ++--- crates/aingle_cortex/src/p2p/dag_sync.rs | 2 +- crates/aingle_cortex/src/proofs/store.rs | 2 +- crates/aingle_cortex/src/rest/audit.rs | 8 +++----- crates/aingle_cortex/src/rest/proof_api.rs | 2 +- crates/aingle_cortex/src/rest/triples.rs | 4 ++-- .../tests/data_integrity_test.rs | 18 ++++++++--------- .../aingle_cortex/tests/rate_limiting_test.rs | 2 +- crates/aingle_graph/src/dag/store.rs | 7 +++---- crates/aingle_graph/src/lib.rs | 11 +++++----- .../tests/graph_integration_tests.rs | 4 ++-- crates/aingle_ingest/src/chunk.rs | 5 ++--- crates/aingle_minimal/src/config.rs | 15 ++++---------- crates/aingle_minimal/src/error.rs | 2 +- crates/aingle_minimal/src/ota.rs | 2 +- .../tests/transport_integration_tests.rs | 1 - crates/aingle_raft/src/log_store.rs | 20 +++++++++---------- crates/aingle_raft/src/network.rs | 2 +- crates/aingle_raft/src/snapshot_builder.rs | 8 ++++---- crates/aingle_raft/src/state_machine.rs | 16 +++++++-------- crates/aingle_viz/src/api.rs | 4 ++-- crates/aingle_viz/src/server.rs | 4 ++-- crates/aingle_wal/src/writer.rs | 12 ++++------- crates/aingle_zk/src/aggregation.rs | 2 +- crates/aingle_zk/src/proof.rs | 2 +- crates/kaneru/benches/agent_bench.rs | 2 +- crates/kaneru/examples/kaneru_orchestrator.rs | 4 ++-- crates/kaneru/src/goal.rs | 2 +- .../kaneru/src/hierarchical/decomposition.rs | 2 +- crates/kaneru/src/hierarchical/goal_solver.rs | 2 +- crates/kaneru/src/hierarchical/tests.rs | 2 +- crates/kaneru/src/predictive/anomaly.rs | 2 +- examples/iot_sensor_network/src/main.rs | 2 +- 40 files changed, 93 insertions(+), 115 deletions(-) diff --git a/crates/aingle_ai/src/kaneru/mod.rs b/crates/aingle_ai/src/kaneru/mod.rs index 22b90603..567a3362 100644 --- a/crates/aingle_ai/src/kaneru/mod.rs +++ b/crates/aingle_ai/src/kaneru/mod.rs @@ -424,7 +424,7 @@ mod tests { let result = agent.query(&query); // Should have some results - assert!(result.relevant_contexts.len() > 0 || result.memory_matches.len() >= 0); + assert!(!result.relevant_contexts.is_empty() || result.memory_matches.len() >= 0); } #[test] diff --git a/crates/aingle_ai/src/nested_learning/mod.rs b/crates/aingle_ai/src/nested_learning/mod.rs index d661db94..a31fd971 100644 --- a/crates/aingle_ai/src/nested_learning/mod.rs +++ b/crates/aingle_ai/src/nested_learning/mod.rs @@ -259,7 +259,7 @@ mod tests { let tx = make_test_tx(1); let result = nested.process(&tx).unwrap(); - assert!(result.processed.features.len() > 0); + assert!(!result.processed.features.is_empty()); } #[test] @@ -267,7 +267,7 @@ mod tests { let config = NestedConfig::default(); let nested = NestedLearning::new(config); - let batch: Vec<_> = (0..10).map(|i| make_test_tx(i)).collect(); + let batch: Vec<_> = (0..10).map(make_test_tx).collect(); let plan = nested.get_validation_plan(&batch); assert_eq!(plan.order.len(), 10); diff --git a/crates/aingle_ai/src/nested_learning/transaction_level.rs b/crates/aingle_ai/src/nested_learning/transaction_level.rs index fd38a931..59d14ed0 100644 --- a/crates/aingle_ai/src/nested_learning/transaction_level.rs +++ b/crates/aingle_ai/src/nested_learning/transaction_level.rs @@ -294,7 +294,7 @@ mod tests { // Process and update let tx = make_tx(1); - let processed = tl.process(tx.clone()); + let _processed = tl.process(tx.clone()); let outcome = ValidationOutcome { valid: true, diff --git a/crates/aingle_ai/src/types.rs b/crates/aingle_ai/src/types.rs index 502a3d3f..613aa182 100644 --- a/crates/aingle_ai/src/types.rs +++ b/crates/aingle_ai/src/types.rs @@ -287,6 +287,6 @@ mod tests { let features = tx.extract_features(); assert_eq!(features.len(), 16); - assert!(features.iter().all(|&f| f >= 0.0 && f <= 2.0)); + assert!(features.iter().all(|&f| (0.0..=2.0).contains(&f))); } } diff --git a/crates/aingle_contracts/src/contract.rs b/crates/aingle_contracts/src/contract.rs index bb437e1c..70a39a04 100644 --- a/crates/aingle_contracts/src/contract.rs +++ b/crates/aingle_contracts/src/contract.rs @@ -14,10 +14,12 @@ use crate::types::{Address, ContractId}; /// Function visibility/mutability #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Default)] pub enum FunctionType { /// Read-only function (view) View, /// State-mutating function + #[default] Mutate, /// Payable function (can receive value) Payable, @@ -27,11 +29,6 @@ pub enum FunctionType { Constructor, } -impl Default for FunctionType { - fn default() -> Self { - Self::Mutate - } -} /// Contract function definition #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/crates/aingle_contracts/src/types.rs b/crates/aingle_contracts/src/types.rs index cc7f6b24..0fcc3b1a 100644 --- a/crates/aingle_contracts/src/types.rs +++ b/crates/aingle_contracts/src/types.rs @@ -46,7 +46,7 @@ impl Address { hasher.update(b"aingle_deploy:"); hasher.update(deployer.as_bytes()); hasher.update(code_hash); - hasher.update(&nonce); + hasher.update(nonce); let hash: [u8; 32] = hasher.finalize().into(); Self(hash) } diff --git a/crates/aingle_cortex/src/cluster_init.rs b/crates/aingle_cortex/src/cluster_init.rs index 2486d014..23dc414c 100644 --- a/crates/aingle_cortex/src/cluster_init.rs +++ b/crates/aingle_cortex/src/cluster_init.rs @@ -103,12 +103,11 @@ impl ClusterConfig { i += 1; } } - "--cluster-tls-key" => { - if i + 1 < args.len() { + "--cluster-tls-key" + if i + 1 < args.len() => { cfg.tls_key = Some(args[i + 1].clone()); i += 1; } - } _ => {} } i += 1; @@ -406,7 +405,7 @@ pub async fn ensure_dag_ready(state: &mut crate::state::AppState, db_path: Optio // Set DAG author from cluster node ID (no-op when not in cluster mode). if let Some(node_id) = state.cluster_node_id { - state.dag_author = Some(aingle_graph::NodeId::named(&format!("node:{}", node_id))); + state.dag_author = Some(aingle_graph::NodeId::named(format!("node:{}", node_id))); } // Initialize the Ed25519 signing key for DAG actions. @@ -459,7 +458,7 @@ pub async fn ensure_dag_ready(state: &mut crate::state::AppState, db_path: Optio } #[cfg(not(unix))] { - if let Err(e) = std::fs::write(&key_path, &seed) { + if let Err(e) = std::fs::write(&key_path, seed) { tracing::error!("Failed to persist DAG signing key: {e}"); } } @@ -496,7 +495,7 @@ pub async fn init_cluster( bind_addr: &str, p2p_addr: &str, ) -> Result<(), Error> { - config.validate().map_err(|e| Error::Internal(e))?; + config.validate().map_err(Error::Internal)?; let wal_dir = config.wal_dir.as_deref().unwrap_or("wal"); let wal_path = std::path::Path::new(wal_dir); diff --git a/crates/aingle_cortex/src/p2p/config.rs b/crates/aingle_cortex/src/p2p/config.rs index c44da9f4..fc1d0320 100644 --- a/crates/aingle_cortex/src/p2p/config.rs +++ b/crates/aingle_cortex/src/p2p/config.rs @@ -114,14 +114,13 @@ impl P2pConfig { i += 1; } } - "--p2p-peer" => { - if i + 1 < args.len() { + "--p2p-peer" + if i + 1 < args.len() => { if let Ok(addr) = args[i + 1].parse::() { cfg.manual_peers.push(addr); } i += 1; } - } _ => {} } i += 1; diff --git a/crates/aingle_cortex/src/p2p/dag_sync.rs b/crates/aingle_cortex/src/p2p/dag_sync.rs index f5f6b0fa..7f9df964 100644 --- a/crates/aingle_cortex/src/p2p/dag_sync.rs +++ b/crates/aingle_cortex/src/p2p/dag_sync.rs @@ -19,7 +19,7 @@ pub fn collect_local_tips(graph: &GraphDB) -> (Vec, u64) { .tips_raw() .unwrap_or_default() .into_iter() - .map(|h| hex::encode(h)) + .map(hex::encode) .collect::>(); let count = dag_store.action_count() as u64; (tips, count) diff --git a/crates/aingle_cortex/src/proofs/store.rs b/crates/aingle_cortex/src/proofs/store.rs index 2bb6eb34..4ff1283e 100644 --- a/crates/aingle_cortex/src/proofs/store.rs +++ b/crates/aingle_cortex/src/proofs/store.rs @@ -282,7 +282,7 @@ impl ProofStore { .map_err(|e| VerificationError::InvalidProofData(e.to_string()))?; self.backend .put(&proof_id, &serialized) - .map_err(|e| VerificationError::InvalidProofData(e))?; + .map_err(VerificationError::InvalidProofData)?; // Update stats let mut stats = self.stats.write().await; diff --git a/crates/aingle_cortex/src/rest/audit.rs b/crates/aingle_cortex/src/rest/audit.rs index 2856ebd9..3f6a8e66 100644 --- a/crates/aingle_cortex/src/rest/audit.rs +++ b/crates/aingle_cortex/src/rest/audit.rs @@ -63,11 +63,9 @@ impl AuditLog { // Read existing entries from JSONL file if let Ok(file) = std::fs::File::open(&path) { let reader = std::io::BufReader::new(file); - for line in reader.lines() { - if let Ok(line) = line { - if let Ok(entry) = serde_json::from_str::(&line) { - entries.push(entry); - } + for line in reader.lines().flatten() { + if let Ok(entry) = serde_json::from_str::(&line) { + entries.push(entry); } } // Keep only the last max_entries diff --git a/crates/aingle_cortex/src/rest/proof_api.rs b/crates/aingle_cortex/src/rest/proof_api.rs index 711aca50..daf030df 100644 --- a/crates/aingle_cortex/src/rest/proof_api.rs +++ b/crates/aingle_cortex/src/rest/proof_api.rs @@ -124,7 +124,7 @@ pub async fn verify_proofs_batch( let results = state.proof_store.batch_verify(&request.proof_ids).await; let mut verifications = Vec::new(); - for (proof_id, result) in request.proof_ids.iter().zip(results.into_iter()) { + for (proof_id, result) in request.proof_ids.iter().zip(results) { // Every entry carries its own replay material: a batch verdict is no more // authoritative than a single one, so it must not be the only thing a // caller can act on. diff --git a/crates/aingle_cortex/src/rest/triples.rs b/crates/aingle_cortex/src/rest/triples.rs index 342b0006..d8a3972f 100644 --- a/crates/aingle_cortex/src/rest/triples.rs +++ b/crates/aingle_cortex/src/rest/triples.rs @@ -178,7 +178,7 @@ pub async fn create_triple( #[cfg(feature = "dag")] if let Some(ref raft) = state.raft { let dag_author = state.dag_author.clone().unwrap_or_else(|| { - aingle_graph::NodeId::named(&format!("node:{}", state.cluster_node_id.unwrap_or(0))) + aingle_graph::NodeId::named(format!("node:{}", state.cluster_node_id.unwrap_or(0))) }); // Get current tips, and a sequence number that continues this author's // recorded chain rather than restarting at 1 and evicting its own history @@ -457,7 +457,7 @@ pub async fn delete_triple( #[cfg(feature = "dag")] if let Some(ref raft) = state.raft { let dag_author = state.dag_author.clone().unwrap_or_else(|| { - aingle_graph::NodeId::named(&format!("node:{}", state.cluster_node_id.unwrap_or(0))) + aingle_graph::NodeId::named(format!("node:{}", state.cluster_node_id.unwrap_or(0))) }); let (parents, subject_for_dag, dag_seq) = { let graph = state.graph.read().await; diff --git a/crates/aingle_cortex/tests/data_integrity_test.rs b/crates/aingle_cortex/tests/data_integrity_test.rs index 70ddb0a7..0a3c6b67 100644 --- a/crates/aingle_cortex/tests/data_integrity_test.rs +++ b/crates/aingle_cortex/tests/data_integrity_test.rs @@ -139,7 +139,7 @@ async fn test_graph_dag_triple_materialization_consistency() { let mut triple_ids = Vec::new(); for i in 0..50 { let triple = Triple::new( - NodeId::named(&format!("entity:{}", i)), + NodeId::named(format!("entity:{}", i)), Predicate::named("has_value"), Value::Integer(i * 100), ); @@ -166,7 +166,7 @@ async fn test_graph_dag_triple_materialization_consistency() { // Verify pattern queries return correct results for i in 0..50 { - let pattern = TriplePattern::subject(NodeId::named(&format!("entity:{}", i))); + let pattern = TriplePattern::subject(NodeId::named(format!("entity:{}", i))); let results = graph.find(pattern).unwrap(); assert_eq!( results.len(), @@ -209,7 +209,7 @@ async fn test_batch_insert_index_consistency() { let triples: Vec = (0..100) .map(|i| { Triple::new( - NodeId::named(&format!("batch:{}", i)), + NodeId::named(format!("batch:{}", i)), Predicate::named("batch_value"), Value::Integer(i), ) @@ -222,7 +222,7 @@ async fn test_batch_insert_index_consistency() { // Verify every triple is findable by subject pattern (uses SPO index) for i in 0..100 { - let pattern = TriplePattern::subject(NodeId::named(&format!("batch:{}", i))); + let pattern = TriplePattern::subject(NodeId::named(format!("batch:{}", i))); let results = graph.find(pattern).unwrap(); assert_eq!(results.len(), 1, "batch:{} should be findable via index", i); assert_eq!(results[0].object, Value::Integer(i)); @@ -238,7 +238,7 @@ async fn test_batch_insert_index_consistency() { let triples2: Vec = (0..100) .map(|i| { Triple::new( - NodeId::named(&format!("batch:{}", i)), + NodeId::named(format!("batch:{}", i)), Predicate::named("batch_value"), Value::Integer(i), ) @@ -270,7 +270,7 @@ async fn test_app_state_flush_restore_roundtrip() { let graph = state.graph.read().await; for i in 0..10 { let triple = Triple::new( - NodeId::named(&format!("node:{}", i)), + NodeId::named(format!("node:{}", i)), Predicate::named("value"), Value::String(format!("data-{}", i)), ); @@ -308,7 +308,7 @@ async fn test_app_state_flush_restore_roundtrip() { assert_eq!(graph.count(), 10, "graph triples should survive restart"); for i in 0..10 { - let pattern = TriplePattern::subject(NodeId::named(&format!("node:{}", i))); + let pattern = TriplePattern::subject(NodeId::named(format!("node:{}", i))); let results = graph.find(pattern).unwrap(); assert_eq!(results.len(), 1, "node:{} missing after restart", i); assert_eq!( @@ -496,7 +496,7 @@ async fn test_graph_sled_persistence_full_cycle() { let graph = GraphDB::sled(path_str).unwrap(); for i in 0..25 { let triple = Triple::new( - NodeId::named(&format!("persist:{}", i)), + NodeId::named(format!("persist:{}", i)), Predicate::named("data"), Value::Float(i as f64 * 1.5), ); @@ -512,7 +512,7 @@ async fn test_graph_sled_persistence_full_cycle() { assert_eq!(graph.count(), 25, "sled graph should persist"); for i in 0..25 { - let pattern = TriplePattern::subject(NodeId::named(&format!("persist:{}", i))); + let pattern = TriplePattern::subject(NodeId::named(format!("persist:{}", i))); let results = graph.find(pattern).unwrap(); assert_eq!(results.len(), 1, "persist:{} missing", i); match &results[0].object { diff --git a/crates/aingle_cortex/tests/rate_limiting_test.rs b/crates/aingle_cortex/tests/rate_limiting_test.rs index 19687210..a18c8972 100644 --- a/crates/aingle_cortex/tests/rate_limiting_test.rs +++ b/crates/aingle_cortex/tests/rate_limiting_test.rs @@ -281,7 +281,7 @@ async fn test_rate_limiter_gradual_refill() { // Should have succeeded ~3 times (with some tolerance for timing) assert!( - successes >= 2 && successes <= 4, + (2..=4).contains(&successes), "Should refill approximately 3 tokens, got {}", successes ); diff --git a/crates/aingle_graph/src/dag/store.rs b/crates/aingle_graph/src/dag/store.rs index b32ea183..27e6563d 100644 --- a/crates/aingle_graph/src/dag/store.rs +++ b/crates/aingle_graph/src/dag/store.rs @@ -713,11 +713,10 @@ impl DagStore { hash.copy_from_slice(&key[2..]); if let Some(action) = DagAction::from_bytes(value) { - if action.timestamp <= *ts { - if best.as_ref().map_or(true, |(_, t)| action.timestamp > *t) { + if action.timestamp <= *ts + && best.as_ref().is_none_or(|(_, t)| action.timestamp > *t) { best = Some((DagActionHash(hash), action.timestamp)); } - } } } @@ -1046,7 +1045,7 @@ fn extract_affected_triple_ids(payload: &DagPayload) -> Vec<[u8; 32]> { match payload { DagPayload::TripleInsert { triples } => triples .iter() - .map(|t| compute_triple_id_from_payload(t)) + .map(compute_triple_id_from_payload) .collect(), DagPayload::TripleDelete { triple_ids, .. } => triple_ids.clone(), DagPayload::Batch { ops } => ops.iter().flat_map(extract_affected_triple_ids).collect(), diff --git a/crates/aingle_graph/src/lib.rs b/crates/aingle_graph/src/lib.rs index ebfa4bfe..b42ccd94 100644 --- a/crates/aingle_graph/src/lib.rs +++ b/crates/aingle_graph/src/lib.rs @@ -1108,11 +1108,10 @@ impl GraphDB { let mut deleted = 0usize; for triple in all { if let Some(name) = triple.subject.as_name() { - if name.starts_with(prefix) { - if self.delete(&triple.id())? { + if name.starts_with(prefix) + && self.delete(&triple.id())? { deleted += 1; } - } } } Ok(deleted) @@ -1456,7 +1455,7 @@ mod tests { for i in 0..10 { db.insert(Triple::new( - NodeId::named(&format!("node:{}", i)), + NodeId::named(format!("node:{}", i)), Predicate::named("has_value"), Value::integer(i as i64), )) @@ -1808,7 +1807,7 @@ mod tests { // DAG has one action let action = db.dag_action(&dag_hash).unwrap().unwrap(); assert_eq!(action.seq, 1); - assert!(action.is_genesis() == false || action.parents.is_empty()); + assert!(!action.is_genesis() || action.parents.is_empty()); // Tips point to the new action let tips = db.dag_tips().unwrap(); @@ -1874,7 +1873,7 @@ mod tests { for seq in 1..=5 { let triple = Triple::new( - NodeId::named(&format!("node:{}", seq)), + NodeId::named(format!("node:{}", seq)), Predicate::named("p"), Value::integer(seq), ); diff --git a/crates/aingle_graph/tests/graph_integration_tests.rs b/crates/aingle_graph/tests/graph_integration_tests.rs index 1029dda2..355e4600 100644 --- a/crates/aingle_graph/tests/graph_integration_tests.rs +++ b/crates/aingle_graph/tests/graph_integration_tests.rs @@ -152,7 +152,7 @@ fn test_batch_insertion_large() { let triples: Vec = (0..100) .map(|i| { Triple::new( - NodeId::named(&format!("node:{}", i)), + NodeId::named(format!("node:{}", i)), Predicate::named("has_id"), Value::integer(i as i64), ) @@ -247,7 +247,7 @@ fn test_query_with_limit() { // Insert 10 triples for i in 0..10 { db.insert(Triple::new( - NodeId::named(&format!("node:{}", i)), + NodeId::named(format!("node:{}", i)), Predicate::named("type"), Value::literal("test"), )) diff --git a/crates/aingle_ingest/src/chunk.rs b/crates/aingle_ingest/src/chunk.rs index dc540327..25324370 100644 --- a/crates/aingle_ingest/src/chunk.rs +++ b/crates/aingle_ingest/src/chunk.rs @@ -188,8 +188,7 @@ mod tests { // 50 lines that are each ~1KB: a full 50-line window would exceed the // cap, so the window must close early on the byte budget. let one = "a".repeat(1024); - let content = std::iter::repeat(one) - .take(50) + let content = std::iter::repeat_n(one, 50) .collect::>() .join("\n"); let chunks = chunk_fixed("data.txt", &content, "h", 50); @@ -240,7 +239,7 @@ mod tests { // Regression: a small markdown note splits by heading into one chunk per // section, none of them byte-split. let content = "# A\n\nalpha\n\n# B\n\nbeta\n"; - let chunks = chunk_markdown("n.md", &content, "h"); + let chunks = chunk_markdown("n.md", content, "h"); assert_eq!(chunks.len(), 2); assert!(chunks[0].text.contains("alpha")); diff --git a/crates/aingle_minimal/src/config.rs b/crates/aingle_minimal/src/config.rs index 94459c36..47043001 100644 --- a/crates/aingle_minimal/src/config.rs +++ b/crates/aingle_minimal/src/config.rs @@ -45,6 +45,7 @@ use std::time::Duration; /// config.power_mode = PowerMode::Low; /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Default)] pub enum PowerMode { /// Full performance with no power-saving measures. /// @@ -54,6 +55,7 @@ pub enum PowerMode { /// A balance between performance and power consumption. /// /// This is the default mode, suitable for most use cases. + #[default] Balanced, /// Prioritizes low power usage by reducing activity. /// @@ -65,11 +67,6 @@ pub enum PowerMode { Critical, } -impl Default for PowerMode { - fn default() -> Self { - Self::Balanced - } -} /// Defines the network transport to be used by the node. /// @@ -321,12 +318,14 @@ impl GossipConfig { /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] +#[derive(Default)] pub enum StorageBackendType { /// Use SQLite for storage. /// /// SQLite is a lightweight, single-file database that's ideal for IoT devices /// and embedded systems. It has minimal dependencies and works well on /// resource-constrained platforms. + #[default] Sqlite, /// Use RocksDB for storage. /// @@ -341,12 +340,6 @@ pub enum StorageBackendType { Memory, } -impl Default for StorageBackendType { - fn default() -> Self { - // SQLite is the default for IoT compatibility. - Self::Sqlite - } -} impl std::fmt::Display for StorageBackendType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/crates/aingle_minimal/src/error.rs b/crates/aingle_minimal/src/error.rs index 4a3249ed..c20f2355 100644 --- a/crates/aingle_minimal/src/error.rs +++ b/crates/aingle_minimal/src/error.rs @@ -740,7 +740,7 @@ mod tests { let _: &dyn StdError = &error; // Test source() for Io error - let io_err = std::io::Error::new(std::io::ErrorKind::Other, "test"); + let io_err = std::io::Error::other("test"); let error = Error::Io(io_err); assert!(StdError::source(&error).is_some()); } diff --git a/crates/aingle_minimal/src/ota.rs b/crates/aingle_minimal/src/ota.rs index 02f44a83..01ac4c7b 100644 --- a/crates/aingle_minimal/src/ota.rs +++ b/crates/aingle_minimal/src/ota.rs @@ -711,7 +711,7 @@ mod tests { }; let cloned = update.clone(); assert_eq!(cloned.version, "2.0.0"); - assert_eq!(cloned.critical, true); + assert!(cloned.critical); let debug = format!("{:?}", update); assert!(debug.contains("UpdateInfo")); diff --git a/crates/aingle_minimal/tests/transport_integration_tests.rs b/crates/aingle_minimal/tests/transport_integration_tests.rs index d406b5ae..ebb4531a 100644 --- a/crates/aingle_minimal/tests/transport_integration_tests.rs +++ b/crates/aingle_minimal/tests/transport_integration_tests.rs @@ -8,7 +8,6 @@ //! - BLE scanning, connection, and message exchange //! - Hardware wallet connection and signing workflow -use std::time::Duration; // ============================================================================ // WebRTC Transport Tests diff --git a/crates/aingle_raft/src/log_store.rs b/crates/aingle_raft/src/log_store.rs index d3dd4d27..b3d6cfde 100644 --- a/crates/aingle_raft/src/log_store.rs +++ b/crates/aingle_raft/src/log_store.rs @@ -258,7 +258,7 @@ impl CortexLogStore { term: *term, data: data.clone(), }) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; } // Only update BTreeMap after all WAL writes succeed @@ -279,7 +279,7 @@ impl CortexLogStore { pub async fn last_log_id(&self) -> Option { let log = self.log.read().await; - log.values().last().map(|e| e.log_id.clone()) + log.values().last().map(|e| e.log_id) } } @@ -299,7 +299,7 @@ impl RaftLogReader for Arc { async fn read_vote(&mut self) -> Result, io::Error> { let v = self.vote.read().await; - Ok(v.clone()) + Ok(*v) } } @@ -318,11 +318,11 @@ impl RaftLogStorage for Arc { let last_log_id = log .values() .last() - .map(|e| e.log_id.clone()) - .or_else(|| purged.clone()); + .map(|e| e.log_id) + .or_else(|| *purged); Ok(LogState { - last_purged_log_id: purged.clone(), + last_purged_log_id: *purged, last_log_id, }) } @@ -334,7 +334,7 @@ impl RaftLogStorage for Arc { async fn save_vote(&mut self, vote: &Vote) -> Result<(), io::Error> { CortexLogStore::persist_vote(&self.wal_dir, vote)?; let mut v = self.vote.write().await; - *v = Some(vote.clone()); + *v = Some(*vote); Ok(()) } @@ -347,7 +347,7 @@ impl RaftLogStorage for Arc { async fn read_committed(&mut self) -> Result, io::Error> { let c = self.committed.read().await; - Ok(c.clone()) + Ok(*c) } async fn append(&mut self, entries: I, callback: IOFlushed) -> Result<(), io::Error> @@ -384,7 +384,7 @@ impl RaftLogStorage for Arc { // Persist truncation boundary so recovery filters out stale entries let mut trunc = self.truncated_after.write().await; - *trunc = last_log_id.clone(); + *trunc = last_log_id; CortexLogStore::persist_truncated_after(&self.wal_dir, &last_log_id)?; Ok(()) @@ -400,7 +400,7 @@ impl RaftLogStorage for Arc { // Persist purge boundary let mut purged = self.purged_log_id.write().await; - *purged = Some(log_id.clone()); + *purged = Some(log_id); CortexLogStore::persist_purged(&self.wal_dir, &log_id)?; // Clean up old WAL segments that are entirely below the purge point diff --git a/crates/aingle_raft/src/network.rs b/crates/aingle_raft/src/network.rs index 0cce196b..453cd8e5 100644 --- a/crates/aingle_raft/src/network.rs +++ b/crates/aingle_raft/src/network.rs @@ -345,7 +345,7 @@ impl CortexNetworkConnection { .as_millis() ); - let num_chunks = (payload.len() + CHUNK_SIZE - 1) / CHUNK_SIZE; + let num_chunks = payload.len().div_ceil(CHUNK_SIZE); tracing::info!( target_node = self.target, total_bytes = total_size, diff --git a/crates/aingle_raft/src/snapshot_builder.rs b/crates/aingle_raft/src/snapshot_builder.rs index d3dff2e8..3ef24a81 100644 --- a/crates/aingle_raft/src/snapshot_builder.rs +++ b/crates/aingle_raft/src/snapshot_builder.rs @@ -36,7 +36,7 @@ impl RaftSnapshotBuilder for CortexSnapshotBuilder { let triples = { let all = graph .find(aingle_graph::TriplePattern::any()) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| io::Error::other(e.to_string()))?; all.into_iter() .map(|t| TripleSnapshot { @@ -49,7 +49,7 @@ impl RaftSnapshotBuilder for CortexSnapshotBuilder { let ineru_ltm = memory .export_snapshot() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| io::Error::other(e.to_string()))?; // Drop locks before serialization to reduce hold time drop(graph); @@ -95,12 +95,12 @@ impl RaftSnapshotBuilder for CortexSnapshotBuilder { let data = snapshot .to_bytes() - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + .map_err(io::Error::other)?; let snapshot_id = format!("snap-{}-{}", last_applied_term, last_applied_index); let meta = SnapshotMeta { - last_log_id: self.last_applied.clone(), + last_log_id: self.last_applied, last_membership: self.last_membership.clone(), snapshot_id, }; diff --git a/crates/aingle_raft/src/state_machine.rs b/crates/aingle_raft/src/state_machine.rs index 64ec27e4..95667adb 100644 --- a/crates/aingle_raft/src/state_machine.rs +++ b/crates/aingle_raft/src/state_machine.rs @@ -428,7 +428,7 @@ impl CortexStateMachine { /// Get the last applied log ID. pub async fn last_applied(&self) -> Option { let guard = self.last_applied.read().await; - guard.clone() + *guard } /// Get the count of applied mutations. @@ -447,7 +447,7 @@ impl RaftStateMachine for Arc { async fn applied_state(&mut self) -> Result<(Option, StoredMembershipOf), io::Error> { let la = self.last_applied.read().await; let membership = self.last_membership.read().await; - Ok((la.clone(), membership.clone())) + Ok((*la, membership.clone())) } async fn apply(&mut self, mut entries: Strm) -> Result<(), io::Error> @@ -460,7 +460,7 @@ impl RaftStateMachine for Arc { // Check for membership change if let Some(membership) = entry.get_membership() { let mut lm = self.last_membership.write().await; - *lm = StoredMembership::new(Some(entry.log_id.clone()), membership.clone()); + *lm = StoredMembership::new(Some(entry.log_id), membership.clone()); } // Apply the business logic @@ -482,7 +482,7 @@ impl RaftStateMachine for Arc { // marking entries as applied before they actually are (#1). { let mut la = self.last_applied.write().await; - *la = Some(entry.log_id.clone()); + *la = Some(entry.log_id); } // Send response to client if waiting (leader only) @@ -500,7 +500,7 @@ impl RaftStateMachine for Arc { CortexSnapshotBuilder { graph: Arc::clone(&self.graph), memory: Arc::clone(&self.memory), - last_applied: la.clone(), + last_applied: *la, last_membership: membership.clone(), proof_provider: self.proof_provider.clone(), } @@ -522,7 +522,7 @@ impl RaftStateMachine for Arc { // Build both new graph and new memory into temporaries FIRST, // then swap atomically only if both succeed (#7). let new_graph = - GraphDB::memory().map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + GraphDB::memory().map_err(|e| io::Error::other(e.to_string()))?; for ts in &cluster_snap.triples { let value = json_to_value(&ts.object); let triple = aingle_graph::Triple::new( @@ -532,7 +532,7 @@ impl RaftStateMachine for Arc { ); new_graph .insert(triple) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + .map_err(|e| io::Error::other(e.to_string()))?; } let new_memory = if !cluster_snap.ineru_ltm.is_empty() { @@ -597,7 +597,7 @@ impl RaftStateMachine for Arc { // Update metadata { let mut la = self.last_applied.write().await; - *la = meta.last_log_id.clone(); + *la = meta.last_log_id; } { let mut lm = self.last_membership.write().await; diff --git a/crates/aingle_viz/src/api.rs b/crates/aingle_viz/src/api.rs index 617dece9..99558bb9 100644 --- a/crates/aingle_viz/src/api.rs +++ b/crates/aingle_viz/src/api.rs @@ -844,8 +844,8 @@ mod tests { // Add test data for i in 0..5 { - let node = DagNodeBuilder::new(&format!("node{}", i), NodeType::Entry) - .label(&format!("Node {}", i)) + let node = DagNodeBuilder::new(format!("node{}", i), NodeType::Entry) + .label(format!("Node {}", i)) .build(); state.add_node(node).await.unwrap(); } diff --git a/crates/aingle_viz/src/server.rs b/crates/aingle_viz/src/server.rs index 25e6418d..c0e4792a 100644 --- a/crates/aingle_viz/src/server.rs +++ b/crates/aingle_viz/src/server.rs @@ -729,8 +729,8 @@ mod tests { let mut dag = DagView::new(); for i in 0..5 { dag.add_node( - DagNodeBuilder::new(&format!("node{}", i), NodeType::Entry) - .label(&format!("Node {}", i)) + DagNodeBuilder::new(format!("node{}", i), NodeType::Entry) + .label(format!("Node {}", i)) .build(), ); } diff --git a/crates/aingle_wal/src/writer.rs b/crates/aingle_wal/src/writer.rs index 13b4cae1..914df15f 100644 --- a/crates/aingle_wal/src/writer.rs +++ b/crates/aingle_wal/src/writer.rs @@ -74,8 +74,7 @@ impl WalWriter { let prev_hash = { let guard = self.last_hash.lock().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, + io::Error::other( format!("WAL last_hash lock poisoned: {e}"), ) })?; @@ -94,8 +93,7 @@ impl WalWriter { { let mut seg = self.current_segment.lock().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, + io::Error::other( format!("WAL segment lock poisoned: {e}"), ) })?; @@ -113,8 +111,7 @@ impl WalWriter { // Update last_hash { let mut guard = self.last_hash.lock().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, + io::Error::other( format!("WAL last_hash lock poisoned: {e}"), ) })?; @@ -127,8 +124,7 @@ impl WalWriter { /// Flush the current segment to disk. pub fn sync(&self) -> io::Result<()> { let mut seg = self.current_segment.lock().map_err(|e| { - io::Error::new( - io::ErrorKind::Other, + io::Error::other( format!("WAL segment lock poisoned: {e}"), ) })?; diff --git a/crates/aingle_zk/src/aggregation.rs b/crates/aingle_zk/src/aggregation.rs index 06aea982..37a49a7b 100644 --- a/crates/aingle_zk/src/aggregation.rs +++ b/crates/aingle_zk/src/aggregation.rs @@ -423,7 +423,7 @@ mod tests { // Should have some size savings from aggregation let savings = agg.size_savings(); - assert!(savings >= 0.0 && savings <= 1.0); + assert!((0.0..=1.0).contains(&savings)); let ratio = agg.compression_ratio(); // Ratio could be < 1.0 if aggregated format has overhead diff --git a/crates/aingle_zk/src/proof.rs b/crates/aingle_zk/src/proof.rs index 3d6d6d0d..0ec00bc1 100644 --- a/crates/aingle_zk/src/proof.rs +++ b/crates/aingle_zk/src/proof.rs @@ -939,7 +939,7 @@ mod tests { // Compute challenge c = H(R || P) let mut hasher = Sha256::new(); - hasher.update(&r_bytes); + hasher.update(r_bytes); hasher.update(public.compress().as_bytes()); let challenge: [u8; 32] = hasher.finalize().into(); let c = Scalar::from_bytes_mod_order(challenge); diff --git a/crates/kaneru/benches/agent_bench.rs b/crates/kaneru/benches/agent_bench.rs index 197a34f5..1234c2d8 100644 --- a/crates/kaneru/benches/agent_bench.rs +++ b/crates/kaneru/benches/agent_bench.rs @@ -5,7 +5,7 @@ //! //! Run with: cargo bench -p kaneru -use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; use kaneru::{Action, Agent, AgentConfig, Condition, Goal, Observation, Rule, SimpleAgent}; /// Benchmark agent creation diff --git a/crates/kaneru/examples/kaneru_orchestrator.rs b/crates/kaneru/examples/kaneru_orchestrator.rs index 3acaacb0..38787e1a 100644 --- a/crates/kaneru/examples/kaneru_orchestrator.rs +++ b/crates/kaneru/examples/kaneru_orchestrator.rs @@ -12,8 +12,8 @@ //! - Serialize/deserialize agent state use kaneru::{ - Action, ActionResult, ActionType, Goal, GoalSelectionStrategy, KaneruAgent, KaneruConfig, - LearningAlgorithm, LearningConfig, Observation, OperationMode, Outcome, PredictiveConfig, + ActionResult, ActionType, Goal, GoalSelectionStrategy, KaneruAgent, KaneruConfig, + LearningAlgorithm, LearningConfig, Observation, OperationMode, Outcome, Priority, }; diff --git a/crates/kaneru/src/goal.rs b/crates/kaneru/src/goal.rs index 1e56ffa1..d2c0b0cf 100644 --- a/crates/kaneru/src/goal.rs +++ b/crates/kaneru/src/goal.rs @@ -375,7 +375,7 @@ mod tests { #[test] fn test_goal_status_clone() { let status = GoalStatus::Active; - let cloned = status.clone(); + let cloned = status; assert_eq!(status, cloned); } diff --git a/crates/kaneru/src/hierarchical/decomposition.rs b/crates/kaneru/src/hierarchical/decomposition.rs index e7ad4998..7726b0cc 100644 --- a/crates/kaneru/src/hierarchical/decomposition.rs +++ b/crates/kaneru/src/hierarchical/decomposition.rs @@ -330,7 +330,7 @@ impl DecompositionStrategy for ConditionalStrategy { #[cfg(test)] mod tests { use super::*; - use crate::types::ValueRange; + #[test] fn test_default_rules_exist() { diff --git a/crates/kaneru/src/hierarchical/goal_solver.rs b/crates/kaneru/src/hierarchical/goal_solver.rs index 4296f40e..2a5158ca 100644 --- a/crates/kaneru/src/hierarchical/goal_solver.rs +++ b/crates/kaneru/src/hierarchical/goal_solver.rs @@ -564,7 +564,7 @@ fn is_mutually_exclusive(g1: &GoalType, g2: &GoalType) -> bool { #[cfg(test)] mod tests { use super::*; - use crate::types::Timestamp; + #[test] fn test_goal_solver_creation() { diff --git a/crates/kaneru/src/hierarchical/tests.rs b/crates/kaneru/src/hierarchical/tests.rs index fe596f56..229b755f 100644 --- a/crates/kaneru/src/hierarchical/tests.rs +++ b/crates/kaneru/src/hierarchical/tests.rs @@ -96,7 +96,7 @@ fn test_executable_goals() { let executable = solver.get_executable_goals(); // Only the simple task and subtask should be executable - assert!(executable.len() >= 1); + assert!(!executable.is_empty()); assert!(executable.iter().any(|g| g.id == id1)); } diff --git a/crates/kaneru/src/predictive/anomaly.rs b/crates/kaneru/src/predictive/anomaly.rs index 57b7fff6..33768bb5 100644 --- a/crates/kaneru/src/predictive/anomaly.rs +++ b/crates/kaneru/src/predictive/anomaly.rs @@ -279,7 +279,7 @@ mod tests { detector.update(&Observation::sensor("flag", true)); detector.update(&Observation::sensor("flag", false)); - assert!(detector.history.len() > 0); + assert!(!detector.history.is_empty()); } #[test] diff --git a/examples/iot_sensor_network/src/main.rs b/examples/iot_sensor_network/src/main.rs index ea457712..499cdf42 100644 --- a/examples/iot_sensor_network/src/main.rs +++ b/examples/iot_sensor_network/src/main.rs @@ -166,7 +166,7 @@ fn main() -> Result<(), Box> { let reading: SensorReading = serde_json::from_slice(&entry.content)?; println!( " {} - {} {:.2} {} at {}", - hash.to_string()[..8].to_string(), + &hash.to_string()[..8], reading.sensor_id, reading.value, reading.unit, From 77acd0867f94fdb0269eb5c86478f73c49041adb Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 10:36:01 +0200 Subject: [PATCH 03/10] style: clear the clippy lints that do not change behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Twenty-one of the twenty-four remaining lints, split by the kind of decision each one represents rather than by the lint that reports it. Notation (15). Ten `Config::default()` values built by assigning fields afterwards become struct literals with `..Default::default()`. One loop that existed only to index an array becomes an iterator. One `unwrap` guarded by a separate `is_some` becomes `if let`. One doc list item that rustdoc read as a continuation gets its blank line. And one more assertion that asserted nothing: `!contexts.is_empty() || matches.len() >= 0` held for every usize, so the test passed even when the query returned nothing — the failure it exists to catch. Descending sorts (6). `sort_by(|a, b| b.k.cmp(&a.k))` becomes `sort_by_key(|x| Reverse(x.k))`, which is stable in the same way and removes the class of bug where `a` and `b` are quietly transposed. All six keys turned out to be `Copy`, so none needed the borrow-shaped exception this pass was ready to grant. Formatting is applied to the files this branch touches and to no others, so the diff stays readable. Three lints are deliberately NOT here, because they are decisions and not cleanup, and burying them in a commit called "style" is how a semantic change ships unreviewed. Both `clamp` sites change NaN behaviour, and the `from_str` one shapes a public API. Each has its own issue. --- crates/aingle_ai/src/ineru/config.rs | 12 ++++--- .../aingle_ai/src/kaneru/continuum_memory.rs | 5 +-- crates/aingle_ai/src/kaneru/mod.rs | 6 ++-- crates/aingle_ai/src/kaneru/self_modifier.rs | 6 ++-- crates/aingle_ai/src/nested_learning/mod.rs | 7 ++-- crates/aingle_ai/src/types.rs | 4 +-- crates/aingle_contracts/src/contract.rs | 4 +-- crates/aingle_cortex/src/cluster_init.rs | 9 +++-- crates/aingle_cortex/src/p2p/config.rs | 11 +++--- crates/aingle_graph/src/dag/mod.rs | 4 +-- crates/aingle_graph/src/dag/store.rs | 22 ++++++------ crates/aingle_graph/src/dag/timetravel.rs | 1 + crates/aingle_graph/src/lib.rs | 7 ++-- crates/aingle_ingest/src/chunk.rs | 4 +-- crates/aingle_minimal/src/config.rs | 12 +++---- crates/aingle_minimal/src/network.rs | 36 ++++++++++++------- .../tests/transport_integration_tests.rs | 1 - crates/aingle_raft/src/log_store.rs | 6 +--- crates/aingle_raft/src/snapshot_builder.rs | 4 +-- crates/aingle_raft/src/state_machine.rs | 3 +- crates/aingle_wal/src/writer.rs | 36 +++++++++---------- crates/kaneru/examples/kaneru_orchestrator.rs | 3 +- crates/kaneru/src/agent.rs | 10 +++--- crates/kaneru/src/coordination.rs | 3 +- .../kaneru/src/hierarchical/decomposition.rs | 1 - crates/kaneru/src/hierarchical/goal_solver.rs | 1 - crates/kaneru/src/learning/engine.rs | 10 +++--- 27 files changed, 118 insertions(+), 110 deletions(-) diff --git a/crates/aingle_ai/src/ineru/config.rs b/crates/aingle_ai/src/ineru/config.rs index b884d75e..779e8cff 100644 --- a/crates/aingle_ai/src/ineru/config.rs +++ b/crates/aingle_ai/src/ineru/config.rs @@ -135,12 +135,16 @@ mod tests { #[test] fn test_validation() { - let mut config = IneruConfig::default(); - config.window_size = 0; + let config = IneruConfig { + window_size: 0, + ..Default::default() + }; assert!(config.validate().is_err()); - config = IneruConfig::default(); - config.surprise_threshold = 1.5; + let config = IneruConfig { + surprise_threshold: 1.5, + ..Default::default() + }; assert!(config.validate().is_err()); } diff --git a/crates/aingle_ai/src/kaneru/continuum_memory.rs b/crates/aingle_ai/src/kaneru/continuum_memory.rs index 4104c47e..828131fe 100644 --- a/crates/aingle_ai/src/kaneru/continuum_memory.rs +++ b/crates/aingle_ai/src/kaneru/continuum_memory.rs @@ -5,6 +5,8 @@ //! //! Non-discrete memory system with smooth interpolation. +use std::cmp::Reverse; + use super::{Experience, MemoryResult, Query}; use crate::types::{pattern_id, Embedding}; @@ -123,8 +125,7 @@ impl ContinuumMemory { pub fn compress(&mut self) { // Keep only top half by access count if self.entries.len() > 100 { - self.entries - .sort_by(|a, b| b.access_count.cmp(&a.access_count)); + self.entries.sort_by_key(|e| Reverse(e.access_count)); self.entries.truncate(self.entries.len() / 2); } } diff --git a/crates/aingle_ai/src/kaneru/mod.rs b/crates/aingle_ai/src/kaneru/mod.rs index 567a3362..ec2f073f 100644 --- a/crates/aingle_ai/src/kaneru/mod.rs +++ b/crates/aingle_ai/src/kaneru/mod.rs @@ -423,8 +423,10 @@ mod tests { }; let result = agent.query(&query); - // Should have some results - assert!(!result.relevant_contexts.is_empty() || result.memory_matches.len() >= 0); + // Should have some results. The second half used to read + // `memory_matches.len() >= 0`, which is true for every usize — so the + // assertion passed even when the query returned nothing at all. + assert!(!result.relevant_contexts.is_empty() || !result.memory_matches.is_empty()); } #[test] diff --git a/crates/aingle_ai/src/kaneru/self_modifier.rs b/crates/aingle_ai/src/kaneru/self_modifier.rs index 2200b30b..1489eaf4 100644 --- a/crates/aingle_ai/src/kaneru/self_modifier.rs +++ b/crates/aingle_ai/src/kaneru/self_modifier.rs @@ -359,8 +359,10 @@ mod tests { #[test] fn test_rule_evolution() { - let mut config = KaneruConfig::default(); - config.max_rules = 10; + let config = KaneruConfig { + max_rules: 10, + ..Default::default() + }; let mut modifier = SelfModifier::new(&config); // Simulate good outcomes to trigger rule creation diff --git a/crates/aingle_ai/src/nested_learning/mod.rs b/crates/aingle_ai/src/nested_learning/mod.rs index a31fd971..7021b764 100644 --- a/crates/aingle_ai/src/nested_learning/mod.rs +++ b/crates/aingle_ai/src/nested_learning/mod.rs @@ -275,8 +275,11 @@ mod tests { #[test] fn test_meta_level_update() { - let mut config = NestedConfig::default(); - config.meta_update_interval = 5; // Update every 5 blocks + // Update every 5 blocks + let config = NestedConfig { + meta_update_interval: 5, + ..Default::default() + }; let mut nested = NestedLearning::new(config); let stats = BlockStats::default(); diff --git a/crates/aingle_ai/src/types.rs b/crates/aingle_ai/src/types.rs index 613aa182..645e58a4 100644 --- a/crates/aingle_ai/src/types.rs +++ b/crates/aingle_ai/src/types.rs @@ -174,8 +174,8 @@ impl AiTransaction { // Entry type hash features (first 4 bytes as float) let type_hash = pattern_id(self.entry_type.as_bytes()); - for i in 0..4 { - features.push(type_hash[i] as f32 / 255.0); + for byte in type_hash.iter().take(4) { + features.push(*byte as f32 / 255.0); } // Agent features (first 4 bytes) diff --git a/crates/aingle_contracts/src/contract.rs b/crates/aingle_contracts/src/contract.rs index 70a39a04..3739df7e 100644 --- a/crates/aingle_contracts/src/contract.rs +++ b/crates/aingle_contracts/src/contract.rs @@ -13,8 +13,7 @@ use crate::error::{ContractError, Result}; use crate::types::{Address, ContractId}; /// Function visibility/mutability -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[derive(Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] pub enum FunctionType { /// Read-only function (view) View, @@ -29,7 +28,6 @@ pub enum FunctionType { Constructor, } - /// Contract function definition #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ContractFunction { diff --git a/crates/aingle_cortex/src/cluster_init.rs b/crates/aingle_cortex/src/cluster_init.rs index 23dc414c..36f5342e 100644 --- a/crates/aingle_cortex/src/cluster_init.rs +++ b/crates/aingle_cortex/src/cluster_init.rs @@ -103,11 +103,10 @@ impl ClusterConfig { i += 1; } } - "--cluster-tls-key" - if i + 1 < args.len() => { - cfg.tls_key = Some(args[i + 1].clone()); - i += 1; - } + "--cluster-tls-key" if i + 1 < args.len() => { + cfg.tls_key = Some(args[i + 1].clone()); + i += 1; + } _ => {} } i += 1; diff --git a/crates/aingle_cortex/src/p2p/config.rs b/crates/aingle_cortex/src/p2p/config.rs index fc1d0320..56cb94ee 100644 --- a/crates/aingle_cortex/src/p2p/config.rs +++ b/crates/aingle_cortex/src/p2p/config.rs @@ -114,13 +114,12 @@ impl P2pConfig { i += 1; } } - "--p2p-peer" - if i + 1 < args.len() => { - if let Ok(addr) = args[i + 1].parse::() { - cfg.manual_peers.push(addr); - } - i += 1; + "--p2p-peer" if i + 1 < args.len() => { + if let Ok(addr) = args[i + 1].parse::() { + cfg.manual_peers.push(addr); } + i += 1; + } _ => {} } i += 1; diff --git a/crates/aingle_graph/src/dag/mod.rs b/crates/aingle_graph/src/dag/mod.rs index 50da412d..970d8ee9 100644 --- a/crates/aingle_graph/src/dag/mod.rs +++ b/crates/aingle_graph/src/dag/mod.rs @@ -29,12 +29,10 @@ pub mod tips; pub use action::{ DagAction, DagActionHash, DagPayload, MemoryOpKind, Provenance, TripleInsertPayload, }; -pub use canonical::{ - CanonicalAction, CANONICAL_SPEC, HASH_ALG, SIGNATURE_ALG, SIGNED_MESSAGE, -}; #[cfg(feature = "sled-backend")] pub use backend::SledDagBackend; pub use backend::{DagBackend, MemoryDagBackend}; +pub use canonical::{CanonicalAction, CANONICAL_SPEC, HASH_ALG, SIGNATURE_ALG, SIGNED_MESSAGE}; pub use export::{DagGraph, ExportFormat}; pub use pruning::{PruneResult, RetentionPolicy}; #[cfg(feature = "dag-sign")] diff --git a/crates/aingle_graph/src/dag/store.rs b/crates/aingle_graph/src/dag/store.rs index 27e6563d..b430cd02 100644 --- a/crates/aingle_graph/src/dag/store.rs +++ b/crates/aingle_graph/src/dag/store.rs @@ -7,6 +7,8 @@ //! In-memory indexes (author chain, affected triples) are rebuilt on startup //! from the backend, ensuring zero data loss across restarts. +use std::cmp::Reverse; + use super::action::{DagAction, DagActionHash, DagPayload, TripleInsertPayload}; use super::backend::DagBackend; use super::pruning::{PruneResult, RetentionPolicy}; @@ -369,7 +371,7 @@ impl DagStore { .collect(); // Sort by seq descending (most recent first) - entries.sort_by(|a, b| b.0.cmp(&a.0)); + entries.sort_by_key(|e| Reverse(e.0)); entries.truncate(limit); drop(idx); @@ -407,7 +409,7 @@ impl DagStore { } // Sort by timestamp descending - result.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); + result.sort_by_key(|a| Reverse(a.timestamp)); result.truncate(limit); Ok(result) @@ -440,7 +442,7 @@ impl DagStore { } } - result.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); + result.sort_by_key(|a| Reverse(a.timestamp)); result.truncate(limit); Ok(result) @@ -714,9 +716,10 @@ impl DagStore { if let Some(action) = DagAction::from_bytes(value) { if action.timestamp <= *ts - && best.as_ref().is_none_or(|(_, t)| action.timestamp > *t) { - best = Some((DagActionHash(hash), action.timestamp)); - } + && best.as_ref().is_none_or(|(_, t)| action.timestamp > *t) + { + best = Some((DagActionHash(hash), action.timestamp)); + } } } @@ -1043,10 +1046,9 @@ fn topo_sort(mut collected: HashMap<[u8; 32], DagAction>) -> crate::Result Vec<[u8; 32]> { match payload { - DagPayload::TripleInsert { triples } => triples - .iter() - .map(compute_triple_id_from_payload) - .collect(), + DagPayload::TripleInsert { triples } => { + triples.iter().map(compute_triple_id_from_payload).collect() + } DagPayload::TripleDelete { triple_ids, .. } => triple_ids.clone(), DagPayload::Batch { ops } => ops.iter().flat_map(extract_affected_triple_ids).collect(), _ => vec![], diff --git a/crates/aingle_graph/src/dag/timetravel.rs b/crates/aingle_graph/src/dag/timetravel.rs index 95abfb82..152885a5 100644 --- a/crates/aingle_graph/src/dag/timetravel.rs +++ b/crates/aingle_graph/src/dag/timetravel.rs @@ -49,6 +49,7 @@ pub struct DagDiff { /// Errors from individual insert/delete operations are intentionally ignored: /// - Duplicate inserts are expected (same triple in multiple actions). /// - Deletes of already-deleted triples are expected after pruning. +/// /// These are not failures — they're inherent to replaying a DAG. pub(crate) fn replay_payload(db: &crate::GraphDB, payload: &DagPayload) -> crate::Result<()> { match payload { diff --git a/crates/aingle_graph/src/lib.rs b/crates/aingle_graph/src/lib.rs index b42ccd94..ef60a331 100644 --- a/crates/aingle_graph/src/lib.rs +++ b/crates/aingle_graph/src/lib.rs @@ -1108,10 +1108,9 @@ impl GraphDB { let mut deleted = 0usize; for triple in all { if let Some(name) = triple.subject.as_name() { - if name.starts_with(prefix) - && self.delete(&triple.id())? { - deleted += 1; - } + if name.starts_with(prefix) && self.delete(&triple.id())? { + deleted += 1; + } } } Ok(deleted) diff --git a/crates/aingle_ingest/src/chunk.rs b/crates/aingle_ingest/src/chunk.rs index 25324370..b79f71f4 100644 --- a/crates/aingle_ingest/src/chunk.rs +++ b/crates/aingle_ingest/src/chunk.rs @@ -188,9 +188,7 @@ mod tests { // 50 lines that are each ~1KB: a full 50-line window would exceed the // cap, so the window must close early on the byte budget. let one = "a".repeat(1024); - let content = std::iter::repeat_n(one, 50) - .collect::>() - .join("\n"); + let content = std::iter::repeat_n(one, 50).collect::>().join("\n"); let chunks = chunk_fixed("data.txt", &content, "h", 50); assert!( diff --git a/crates/aingle_minimal/src/config.rs b/crates/aingle_minimal/src/config.rs index 47043001..dc5a6e75 100644 --- a/crates/aingle_minimal/src/config.rs +++ b/crates/aingle_minimal/src/config.rs @@ -44,8 +44,7 @@ use std::time::Duration; /// let mut config = Config::test_mode(); /// config.power_mode = PowerMode::Low; /// ``` -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -#[derive(Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] pub enum PowerMode { /// Full performance with no power-saving measures. /// @@ -67,7 +66,6 @@ pub enum PowerMode { Critical, } - /// Defines the network transport to be used by the node. /// /// Different transports are optimized for different environments. CoAP is ideal @@ -340,7 +338,6 @@ pub enum StorageBackendType { Memory, } - impl std::fmt::Display for StorageBackendType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -1020,8 +1017,11 @@ mod tests { #[test] fn test_config_validate_invalid_memory() { - let mut config = Config::default(); - config.memory_limit = 100; // Too small + // Too small + let config = Config { + memory_limit: 100, + ..Default::default() + }; assert!(config.validate().is_err()); } diff --git a/crates/aingle_minimal/src/network.rs b/crates/aingle_minimal/src/network.rs index a2d31677..15fd7e1d 100644 --- a/crates/aingle_minimal/src/network.rs +++ b/crates/aingle_minimal/src/network.rs @@ -12,6 +12,8 @@ //! - **QUIC**: Future support for reliable transport //! - **Mesh**: Future support for mesh networking +use std::cmp::Reverse; + use crate::config::{GossipConfig, TransportConfig}; use crate::discovery::Discovery; use crate::error::{Error, Result}; @@ -565,7 +567,7 @@ impl Network { /// Get peers for gossip (best quality first) pub fn gossip_peers(&self) -> Vec { let mut peers: Vec<_> = self.active_peers().into_iter().collect(); - peers.sort_by(|a, b| b.quality.cmp(&a.quality)); + peers.sort_by_key(|p| Reverse(p.quality)); peers .into_iter() .take(self.gossip_config.max_peers) @@ -905,8 +907,10 @@ mod tests { #[test] fn test_gossip_timing() { - let mut config = GossipConfig::default(); - config.loop_delay = Duration::from_millis(10); + let config = GossipConfig { + loop_delay: Duration::from_millis(10), + ..Default::default() + }; let mut gossip = GossipManager::new(config); assert!(!gossip.should_gossip()); // Just created @@ -1166,8 +1170,10 @@ mod tests { #[test] fn test_network_gossip_peers_sorting() { let config = TransportConfig::Memory; - let mut gossip = GossipConfig::default(); - gossip.max_peers = 10; + let gossip = GossipConfig { + max_peers: 10, + ..Default::default() + }; let mut network = Network::new(config, gossip, "test-node".to_string()); // Add multiple peers @@ -1189,8 +1195,10 @@ mod tests { #[test] fn test_network_gossip_peers_limited() { let config = TransportConfig::Memory; - let mut gossip = GossipConfig::default(); - gossip.max_peers = 2; + let gossip = GossipConfig { + max_peers: 2, + ..Default::default() + }; let mut network = Network::new(config, gossip, "test-node".to_string()); // Add more peers than limit @@ -1260,8 +1268,10 @@ mod tests { #[test] fn test_gossip_manager_complete_failure() { - let mut config = GossipConfig::default(); - config.loop_delay = Duration::from_millis(10); + let config = GossipConfig { + loop_delay: Duration::from_millis(10), + ..Default::default() + }; let mut manager = GossipManager::new(config); @@ -1589,9 +1599,11 @@ mod tests { #[test] fn test_gossip_manager_config() { - let mut config = GossipConfig::default(); - config.loop_delay = Duration::from_millis(100); - config.max_peers = 5; + let config = GossipConfig { + loop_delay: Duration::from_millis(100), + max_peers: 5, + ..Default::default() + }; let manager = GossipManager::new(config.clone()); diff --git a/crates/aingle_minimal/tests/transport_integration_tests.rs b/crates/aingle_minimal/tests/transport_integration_tests.rs index ebb4531a..5c2de25d 100644 --- a/crates/aingle_minimal/tests/transport_integration_tests.rs +++ b/crates/aingle_minimal/tests/transport_integration_tests.rs @@ -8,7 +8,6 @@ //! - BLE scanning, connection, and message exchange //! - Hardware wallet connection and signing workflow - // ============================================================================ // WebRTC Transport Tests // ============================================================================ diff --git a/crates/aingle_raft/src/log_store.rs b/crates/aingle_raft/src/log_store.rs index b3d6cfde..a19d495c 100644 --- a/crates/aingle_raft/src/log_store.rs +++ b/crates/aingle_raft/src/log_store.rs @@ -315,11 +315,7 @@ impl RaftLogStorage for Arc { let log = self.log.read().await; let purged = self.purged_log_id.read().await; - let last_log_id = log - .values() - .last() - .map(|e| e.log_id) - .or_else(|| *purged); + let last_log_id = log.values().last().map(|e| e.log_id).or_else(|| *purged); Ok(LogState { last_purged_log_id: *purged, diff --git a/crates/aingle_raft/src/snapshot_builder.rs b/crates/aingle_raft/src/snapshot_builder.rs index 3ef24a81..d15a3244 100644 --- a/crates/aingle_raft/src/snapshot_builder.rs +++ b/crates/aingle_raft/src/snapshot_builder.rs @@ -93,9 +93,7 @@ impl RaftSnapshotBuilder for CortexSnapshotBuilder { checksum: String::new(), }; - let data = snapshot - .to_bytes() - .map_err(io::Error::other)?; + let data = snapshot.to_bytes().map_err(io::Error::other)?; let snapshot_id = format!("snap-{}-{}", last_applied_term, last_applied_index); diff --git a/crates/aingle_raft/src/state_machine.rs b/crates/aingle_raft/src/state_machine.rs index 95667adb..c1f6cb8d 100644 --- a/crates/aingle_raft/src/state_machine.rs +++ b/crates/aingle_raft/src/state_machine.rs @@ -521,8 +521,7 @@ impl RaftStateMachine for Arc { // Build both new graph and new memory into temporaries FIRST, // then swap atomically only if both succeed (#7). - let new_graph = - GraphDB::memory().map_err(|e| io::Error::other(e.to_string()))?; + let new_graph = GraphDB::memory().map_err(|e| io::Error::other(e.to_string()))?; for ts in &cluster_snap.triples { let value = json_to_value(&ts.object); let triple = aingle_graph::Triple::new( diff --git a/crates/aingle_wal/src/writer.rs b/crates/aingle_wal/src/writer.rs index 914df15f..eb35e9b1 100644 --- a/crates/aingle_wal/src/writer.rs +++ b/crates/aingle_wal/src/writer.rs @@ -73,11 +73,10 @@ impl WalWriter { let timestamp = Utc::now(); let prev_hash = { - let guard = self.last_hash.lock().map_err(|e| { - io::Error::other( - format!("WAL last_hash lock poisoned: {e}"), - ) - })?; + let guard = self + .last_hash + .lock() + .map_err(|e| io::Error::other(format!("WAL last_hash lock poisoned: {e}")))?; *guard }; @@ -92,11 +91,10 @@ impl WalWriter { }; { - let mut seg = self.current_segment.lock().map_err(|e| { - io::Error::other( - format!("WAL segment lock poisoned: {e}"), - ) - })?; + let mut seg = self + .current_segment + .lock() + .map_err(|e| io::Error::other(format!("WAL segment lock poisoned: {e}")))?; seg.append(&entry)?; seg.sync()?; @@ -110,11 +108,10 @@ impl WalWriter { // Update last_hash { - let mut guard = self.last_hash.lock().map_err(|e| { - io::Error::other( - format!("WAL last_hash lock poisoned: {e}"), - ) - })?; + let mut guard = self + .last_hash + .lock() + .map_err(|e| io::Error::other(format!("WAL last_hash lock poisoned: {e}")))?; *guard = entry.hash; } @@ -123,11 +120,10 @@ impl WalWriter { /// Flush the current segment to disk. pub fn sync(&self) -> io::Result<()> { - let mut seg = self.current_segment.lock().map_err(|e| { - io::Error::other( - format!("WAL segment lock poisoned: {e}"), - ) - })?; + let mut seg = self + .current_segment + .lock() + .map_err(|e| io::Error::other(format!("WAL segment lock poisoned: {e}")))?; seg.sync() } diff --git a/crates/kaneru/examples/kaneru_orchestrator.rs b/crates/kaneru/examples/kaneru_orchestrator.rs index 38787e1a..3b5f2d66 100644 --- a/crates/kaneru/examples/kaneru_orchestrator.rs +++ b/crates/kaneru/examples/kaneru_orchestrator.rs @@ -13,8 +13,7 @@ use kaneru::{ ActionResult, ActionType, Goal, GoalSelectionStrategy, KaneruAgent, KaneruConfig, - LearningAlgorithm, LearningConfig, Observation, OperationMode, Outcome, - Priority, + LearningAlgorithm, LearningConfig, Observation, OperationMode, Outcome, Priority, }; fn main() { diff --git a/crates/kaneru/src/agent.rs b/crates/kaneru/src/agent.rs index c935fff2..b1624975 100644 --- a/crates/kaneru/src/agent.rs +++ b/crates/kaneru/src/agent.rs @@ -872,10 +872,12 @@ impl Agent for SimpleAgent { self.stats.actions_executed += 1; // Store state-action pair for learning - if self.config.learning_enabled && self.last_observation.is_some() { - let state_id = StateId::from_observation(self.last_observation.as_ref().unwrap()); - let action_id = ActionId::from_action(&action); - self.last_state_action = Some((state_id, action_id)); + if self.config.learning_enabled { + if let Some(observation) = self.last_observation.as_ref() { + let state_id = StateId::from_observation(observation); + let action_id = ActionId::from_action(&action); + self.last_state_action = Some((state_id, action_id)); + } } // Simple execution - just log and return success diff --git a/crates/kaneru/src/coordination.rs b/crates/kaneru/src/coordination.rs index 2ec8f594..b2850418 100644 --- a/crates/kaneru/src/coordination.rs +++ b/crates/kaneru/src/coordination.rs @@ -36,6 +36,7 @@ use crate::{Action, AgentId, KaneruAgent, Observation, Outcome}; use serde::{Deserialize, Serialize}; +use std::cmp::Reverse; use std::collections::{HashMap, VecDeque}; /// A unique identifier for a `Message`. @@ -301,7 +302,7 @@ impl MessageBus { } self.queue = remaining; - messages.sort_by(|a, b| b.priority.cmp(&a.priority)); + messages.sort_by_key(|m| Reverse(m.priority)); messages } diff --git a/crates/kaneru/src/hierarchical/decomposition.rs b/crates/kaneru/src/hierarchical/decomposition.rs index 7726b0cc..773b159f 100644 --- a/crates/kaneru/src/hierarchical/decomposition.rs +++ b/crates/kaneru/src/hierarchical/decomposition.rs @@ -330,7 +330,6 @@ impl DecompositionStrategy for ConditionalStrategy { #[cfg(test)] mod tests { use super::*; - #[test] fn test_default_rules_exist() { diff --git a/crates/kaneru/src/hierarchical/goal_solver.rs b/crates/kaneru/src/hierarchical/goal_solver.rs index 2a5158ca..6abcb834 100644 --- a/crates/kaneru/src/hierarchical/goal_solver.rs +++ b/crates/kaneru/src/hierarchical/goal_solver.rs @@ -564,7 +564,6 @@ fn is_mutually_exclusive(g1: &GoalType, g2: &GoalType) -> bool { #[cfg(test)] mod tests { use super::*; - #[test] fn test_goal_solver_creation() { diff --git a/crates/kaneru/src/learning/engine.rs b/crates/kaneru/src/learning/engine.rs index a78328f1..bb813b5e 100644 --- a/crates/kaneru/src/learning/engine.rs +++ b/crates/kaneru/src/learning/engine.rs @@ -670,10 +670,12 @@ mod tests { #[test] fn test_epsilon_decay() { - let mut config = LearningConfig::default(); - config.epsilon = 1.0; - config.epsilon_decay = 0.9; - config.epsilon_min = 0.1; + let config = LearningConfig { + epsilon: 1.0, + epsilon_decay: 0.9, + epsilon_min: 0.1, + ..Default::default() + }; let mut engine = LearningEngine::new(config); From 00854436ba99e439b3642718a51681d4e9d628fc Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 10:53:08 +0200 Subject: [PATCH 04/10] ci: enforce the lint bar the project already declares MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CLAUDE.md defines `make lint` as `cargo clippy --workspace --all-targets -- -D warnings`. CI ran `cargo clippy -p aingle_minimal --features rest -- -W clippy::all`: one member of seventeen, the library target only, and warnings that failed nothing. It reported green throughout. Behind it: a benchmark that had not compiled in months, assertions that compared unsigned counters against zero and so passed no matter what the code did, and a `flatten()` over `io::Lines` that spins forever on a repeated read error. `--all-targets` is what covers benches and integration tests, and is the only reason the dead benchmark surfaced. `Format Check` was separately red on main, so the aggregate `CI Success` gate has been failing for some time; `cargo fmt --all` here makes it pass and the widened clippy keeps it honest. Note the lints were an onion: with `-D warnings` a failing crate stops everything downstream from being linted at all, so the count grew from 24 to 54 as each layer was cleared. It took four passes to reach a clean run — worth knowing before trusting a single measurement. The three lints that are decisions rather than cleanup carry `#[allow(...)]` with the full reasoning at the site: two `clamp` substitutions that change NaN behaviour, and a public `from_str` that shadows the trait method. Removing each attribute is the definition of done for that decision. --- .github/workflows/ci.yml | 20 ++++++++- crates/aingle_ai/src/ineru/surprise_gate.rs | 15 +++++++ .../src/nested_learning/transaction_level.rs | 11 ++++- crates/aingle_cortex/src/main.rs | 6 ++- crates/aingle_cortex/src/p2p/config.rs | 12 ++++-- crates/aingle_cortex/src/p2p/manager.rs | 10 +++-- crates/aingle_cortex/src/rest/audit.rs | 8 +++- crates/aingle_cortex/src/rest/proof_api.rs | 4 +- crates/aingle_cortex/src/service/ingest.rs | 41 +++++++++++++++---- crates/aingle_cortex/src/service/tasks.rs | 39 ++++++++++++++---- .../tests/data_integrity_test.rs | 4 +- crates/aingle_graph/src/dag/export.rs | 10 +++++ crates/aingle_ingest/src/lib.rs | 36 ++++++++++++---- crates/aingle_ingest/src/tasks.rs | 22 ++++++++-- crates/aingle_logic/src/engine.rs | 2 +- crates/aingle_logic/src/rule.rs | 4 +- .../tests/e2e_multi_node_tests.rs | 6 +-- crates/aingle_viz/src/dag.rs | 4 +- 18 files changed, 201 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 697eb6cb..8158b9f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,8 +86,26 @@ jobs: restore-keys: | ${{ runner.os }}-cargo-clippy- + # The bar CLAUDE.md declares as `make lint`, which nothing enforced. + # + # This step used to read `-p aingle_minimal --features rest -- -W + # clippy::all`: one crate of seventeen, the library target only, and + # warnings that did not fail anything. It reported green while 54 lints + # accumulated across the workspace — among them a benchmark that had not + # compiled in months, and assertions that compared unsigned counters + # against zero and therefore passed no matter what the code did. + # + # `--all-targets` is load-bearing, not thoroughness for its own sake: it + # is what covers tests, benches and examples, and it is the only reason + # the dead benchmark was found at all. - name: Run Clippy - run: cargo clippy -p aingle_minimal --features rest -- -W clippy::all + run: cargo clippy --workspace --all-targets -- -D warnings + + # No separate "did it really compile?" step: with `--workspace` and + # `-D warnings`, a member that fails to build makes THIS step exit + # non-zero. The companion-repo failure that inspired one was a job that + # went red for a lint and stayed red for a build script nobody read to + # the bottom of — a reading problem, not a missing check. # Build check build: diff --git a/crates/aingle_ai/src/ineru/surprise_gate.rs b/crates/aingle_ai/src/ineru/surprise_gate.rs index 48a21b8f..677eccd0 100644 --- a/crates/aingle_ai/src/ineru/surprise_gate.rs +++ b/crates/aingle_ai/src/ineru/surprise_gate.rs @@ -130,6 +130,21 @@ impl SurpriseGate { } /// Get adaptive threshold based on recent statistics + /// + /// NOT `clamp`, and not by accident. `f32::min(NaN, 1.0)` returns `1.0` — + /// `min`/`max` discard NaN and return the other operand — so a NaN mean or + /// std yields a threshold of `1.0` here. `NaN.clamp(0.1, 1.0)` returns NaN, + /// and a NaN threshold is worse than either bound: every comparison against + /// it is false, so the gate would stop firing and do so silently. + /// `get_std()` can produce NaN from an empty history or from a negative + /// variance caused by floating-point error. + /// + /// The right fix is neither expression: branch on `is_nan()` explicitly and + /// pin the chosen fallback with a test. Which fallback is correct — today's + /// 1.0, meaning the gate almost never fires, or 0.1, meaning it almost + /// always does — belongs to whoever owns this gate, so it is left as a + /// decision rather than made silently by a lint fix. + #[allow(clippy::manual_clamp)] pub fn adaptive_threshold(&self) -> f32 { // Use mean + 1 std as adaptive threshold let std = self.get_std(); diff --git a/crates/aingle_ai/src/nested_learning/transaction_level.rs b/crates/aingle_ai/src/nested_learning/transaction_level.rs index 59d14ed0..19d8350d 100644 --- a/crates/aingle_ai/src/nested_learning/transaction_level.rs +++ b/crates/aingle_ai/src/nested_learning/transaction_level.rs @@ -198,7 +198,16 @@ impl TransactionClassifier { let min_dist = sorted.first().copied().unwrap_or(1.0); let second_dist = sorted.get(1).copied().unwrap_or(min_dist); - // Confidence based on separation + // Confidence based on separation. + // + // NOT `clamp`: `max`/`min` discard NaN and `clamp` propagates it, so the + // two differ exactly when the input is NaN. Here the division is guarded + // by `second_dist > 0.0`, so NaN should be unreachable — but that rests + // on the `partial_cmp().unwrap()` in the sort above, which PANICS on a + // NaN distance rather than producing one. Substituting `clamp` would be + // safe and would also settle nothing; the real work is making that sort + // total (`total_cmp`) and then deciding what a NaN distance means. + #[allow(clippy::manual_clamp)] if second_dist > 0.0 { (1.0 - min_dist / second_dist).max(0.0).min(1.0) } else { diff --git a/crates/aingle_cortex/src/main.rs b/crates/aingle_cortex/src/main.rs index 374b60f0..90bbc0e0 100644 --- a/crates/aingle_cortex/src/main.rs +++ b/crates/aingle_cortex/src/main.rs @@ -41,8 +41,10 @@ async fn main() -> Result<(), Box> { return Ok(()); } - let mut config = CortexConfig::default(); - config.embed_model = std::env::var("AINGLE_EMBED_MODEL").ok(); + let mut config = CortexConfig { + embed_model: std::env::var("AINGLE_EMBED_MODEL").ok(), + ..Default::default() + }; // Simple argument parsing let mut i = 1; diff --git a/crates/aingle_cortex/src/p2p/config.rs b/crates/aingle_cortex/src/p2p/config.rs index 56cb94ee..4868c854 100644 --- a/crates/aingle_cortex/src/p2p/config.rs +++ b/crates/aingle_cortex/src/p2p/config.rs @@ -139,8 +139,10 @@ mod tests { #[test] fn rejects_invalid_port() { - let mut cfg = P2pConfig::default(); - cfg.port = 0; + let mut cfg = P2pConfig { + port: 0, + ..Default::default() + }; assert!(cfg.validate().is_err()); cfg.port = 80; @@ -175,8 +177,10 @@ mod tests { #[test] fn rejects_empty_seed() { - let mut cfg = P2pConfig::default(); - cfg.seed = Some(String::new()); + let cfg = P2pConfig { + seed: Some(String::new()), + ..Default::default() + }; assert!(cfg.validate().is_err()); } } diff --git a/crates/aingle_cortex/src/p2p/manager.rs b/crates/aingle_cortex/src/p2p/manager.rs index f6912bea..461c1786 100644 --- a/crates/aingle_cortex/src/p2p/manager.rs +++ b/crates/aingle_cortex/src/p2p/manager.rs @@ -1057,10 +1057,12 @@ mod tests { #[tokio::test] async fn manager_starts_and_stops() { - let mut config = P2pConfig::default(); - config.enabled = true; - config.port = 0; // OS-assigned - config.data_dir = tempfile::TempDir::new().unwrap().into_path(); + let config = P2pConfig { + enabled: true, + port: 0, // OS-assigned + data_dir: tempfile::TempDir::new().unwrap().keep(), + ..Default::default() + }; let state = AppState::new().unwrap(); let manager = P2pManager::start(config, state).await.unwrap(); diff --git a/crates/aingle_cortex/src/rest/audit.rs b/crates/aingle_cortex/src/rest/audit.rs index 3f6a8e66..3b25af9e 100644 --- a/crates/aingle_cortex/src/rest/audit.rs +++ b/crates/aingle_cortex/src/rest/audit.rs @@ -63,7 +63,7 @@ impl AuditLog { // Read existing entries from JSONL file if let Ok(file) = std::fs::File::open(&path) { let reader = std::io::BufReader::new(file); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if let Ok(entry) = serde_json::from_str::(&line) { entries.push(entry); } @@ -188,6 +188,10 @@ impl AuditLog { pub fn len(&self) -> usize { self.entries.len() } + + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } } impl Default for AuditLog { @@ -298,7 +302,7 @@ mod tests { } // Should have evicted some entries assert!(log.len() <= 15); - assert!(log.len() > 0); + assert!(!log.is_empty()); } #[test] diff --git a/crates/aingle_cortex/src/rest/proof_api.rs b/crates/aingle_cortex/src/rest/proof_api.rs index daf030df..e8ba89a0 100644 --- a/crates/aingle_cortex/src/rest/proof_api.rs +++ b/crates/aingle_cortex/src/rest/proof_api.rs @@ -498,7 +498,7 @@ mod tests { proof_data: serde_json::json!({"test": "data"}), metadata: None, }; - submit_proof(AxumState(state.clone()), None, Json(request)) + let _ = submit_proof(AxumState(state.clone()), None, Json(request)) .await .unwrap(); } @@ -526,7 +526,7 @@ mod tests { metadata: None, }; - submit_proof(AxumState(state.clone()), None, Json(request)) + let _ = submit_proof(AxumState(state.clone()), None, Json(request)) .await .unwrap(); diff --git a/crates/aingle_cortex/src/service/ingest.rs b/crates/aingle_cortex/src/service/ingest.rs index 05169000..7dda89c6 100644 --- a/crates/aingle_cortex/src/service/ingest.rs +++ b/crates/aingle_cortex/src/service/ingest.rs @@ -1358,19 +1358,37 @@ mod tests { let state = enabled_state().await; // Two open tasks. - write(dir.path(), "todos.md", "# Todos\n\n- [ ] Task A\n- [ ] Task B\n"); + write( + dir.path(), + "todos.md", + "# Todos\n\n- [ ] Task A\n- [ ] Task B\n", + ); ingest_path(&state, path, None).await.unwrap(); let rows = list_tasks(&state, None).await; assert_eq!(rows.len(), 2); assert!(rows.iter().all(|r| r.status == "todo")); // Complete A, keep B — re-ingest the changed note. - write(dir.path(), "todos.md", "# Todos\n\n- [x] Task A\n- [ ] Task B\n"); + write( + dir.path(), + "todos.md", + "# Todos\n\n- [x] Task A\n- [ ] Task B\n", + ); ingest_path(&state, path, None).await.unwrap(); let rows = list_tasks(&state, None).await; - assert_eq!(rows.len(), 2, "still exactly two tasks — no orphans or duplicates"); - assert_eq!(rows.iter().find(|r| r.text == "Task A").unwrap().status, "done"); - assert_eq!(rows.iter().find(|r| r.text == "Task B").unwrap().status, "todo"); + assert_eq!( + rows.len(), + 2, + "still exactly two tasks — no orphans or duplicates" + ); + assert_eq!( + rows.iter().find(|r| r.text == "Task A").unwrap().status, + "done" + ); + assert_eq!( + rows.iter().find(|r| r.text == "Task B").unwrap().status, + "todo" + ); // The old `status=todo` triple for A must be gone (exactly one remains). { @@ -1389,7 +1407,11 @@ mod tests { .with_predicate(Predicate::named("status")), ) .unwrap(); - assert_eq!(statuses.len(), 1, "no stale status triple should remain for A"); + assert_eq!( + statuses.len(), + 1, + "no stale status triple should remain for A" + ); } // Remove A from the note — its task node is retracted, B survives. @@ -1425,9 +1447,10 @@ mod tests { assert_eq!(rows.len(), 2); // Snapshot Q2's `card_due` triple id — it must NOT change when only Q1 is reviewed. - let q2_due_id_before = card_field_triple_id(&state, "card:deck.md#bbbbbbbbbbbb", "card_due") - .await - .expect("Q2 card_due before"); + let q2_due_id_before = + card_field_triple_id(&state, "card:deck.md#bbbbbbbbbbbb", "card_due") + .await + .expect("Q2 card_due before"); // Review Q1: reschedule it (new due + ef), keep its front text and id. write( diff --git a/crates/aingle_cortex/src/service/tasks.rs b/crates/aingle_cortex/src/service/tasks.rs index 6c0a0422..b781ed0f 100644 --- a/crates/aingle_cortex/src/service/tasks.rs +++ b/crates/aingle_cortex/src/service/tasks.rs @@ -244,7 +244,11 @@ mod tests { vec![ (format!("task:n.md#{id}"), "is_a".into(), "task".into()), (format!("task:n.md#{id}"), "status".into(), status.into()), - (format!("task:n.md#{id}"), "task_text".into(), id.to_uppercase()), + ( + format!("task:n.md#{id}"), + "task_text".into(), + id.to_uppercase(), + ), (format!("task:n.md#{id}"), date_pred.into(), date.into()), (format!("task:n.md#{id}"), "in_note".into(), "n.md".into()), ] @@ -258,7 +262,7 @@ mod tests { rows.extend(task("c", "todo", "deadline", "2026-07-28")); // upcoming (≤+7) rows.extend(task("d", "todo", "deadline", "2026-08-30")); // beyond horizon rows.extend(task("e", "done", "deadline", "2026-07-20")); // excluded (done) - // f: open but undated → excluded from agenda + // f: open but undated → excluded from agenda rows.push(("task:n.md#f".into(), "is_a".into(), "task".into())); rows.push(("task:n.md#f".into(), "status".into(), "todo".into())); rows.push(("task:n.md#f".into(), "task_text".into(), "F".into())); @@ -270,9 +274,24 @@ mod tests { let state = graph_with(&refs).await; let ag = super::agenda(&state, "2026-07-24", 7).await; - assert_eq!(ag.overdue.iter().map(|t| t.text.as_str()).collect::>(), ["A"]); - assert_eq!(ag.today.iter().map(|t| t.text.as_str()).collect::>(), ["B"]); - assert_eq!(ag.upcoming.iter().map(|t| t.text.as_str()).collect::>(), ["C"]); + assert_eq!( + ag.overdue + .iter() + .map(|t| t.text.as_str()) + .collect::>(), + ["A"] + ); + assert_eq!( + ag.today.iter().map(|t| t.text.as_str()).collect::>(), + ["B"] + ); + assert_eq!( + ag.upcoming + .iter() + .map(|t| t.text.as_str()) + .collect::>(), + ["C"] + ); } #[tokio::test] @@ -290,7 +309,10 @@ mod tests { let all = super::list_tasks(&state, None).await; assert_eq!(all.len(), 3); // sorted by due date: b(07-10) < a(07-20) < c(07-25) - assert_eq!(all.iter().map(|t| t.text.as_str()).collect::>(), ["B", "A", "C"]); + assert_eq!( + all.iter().map(|t| t.text.as_str()).collect::>(), + ["B", "A", "C"] + ); let doing = super::list_tasks(&state, Some("doing")).await; assert_eq!(doing.len(), 1); @@ -311,7 +333,10 @@ mod tests { ]) .await; let ag = super::agenda(&state, "2026-07-24", 7).await; - assert!(ag.today.iter().any(|t| t.text == "Z"), "scheduled-today belongs in today"); + assert!( + ag.today.iter().any(|t| t.text == "Z"), + "scheduled-today belongs in today" + ); assert!(!ag.upcoming.iter().any(|t| t.text == "Z")); } diff --git a/crates/aingle_cortex/tests/data_integrity_test.rs b/crates/aingle_cortex/tests/data_integrity_test.rs index 0a3c6b67..e2253f0d 100644 --- a/crates/aingle_cortex/tests/data_integrity_test.rs +++ b/crates/aingle_cortex/tests/data_integrity_test.rs @@ -185,8 +185,8 @@ async fn test_graph_dag_triple_materialization_consistency() { assert_eq!(graph.count(), 25); // Verify even ones remain, odd ones gone - for i in 0..50 { - let exists = graph.get(&triple_ids[i]).unwrap().is_some(); + for (i, id) in triple_ids.iter().enumerate().take(50) { + let exists = graph.get(id).unwrap().is_some(); if i % 2 == 0 { assert!(exists, "even triple {} should still exist", i); } else { diff --git a/crates/aingle_graph/src/dag/export.rs b/crates/aingle_graph/src/dag/export.rs index c8c0190e..11b02bc2 100644 --- a/crates/aingle_graph/src/dag/export.rs +++ b/crates/aingle_graph/src/dag/export.rs @@ -42,6 +42,16 @@ pub enum ExportFormat { impl ExportFormat { /// Parse from string (case-insensitive). + /// + /// Shadows `std::str::FromStr::from_str`, which clippy rightly flags: a + /// caller writing `ExportFormat::from_str` cannot tell which one they get, + /// and this one returns `Option` where the trait returns `Result`. + /// + /// The fix is to implement `FromStr` properly with an error type and drop + /// this inherent method. It is cheap — there is exactly one caller outside + /// this module's own tests — but it changes a public signature, so it is a + /// deliberate change with its own review and not a line in a lint sweep. + #[allow(clippy::should_implement_trait)] pub fn from_str(s: &str) -> Option { match s.to_lowercase().as_str() { "dot" | "graphviz" => Some(Self::Dot), diff --git a/crates/aingle_ingest/src/lib.rs b/crates/aingle_ingest/src/lib.rs index ef339115..ee41b25b 100644 --- a/crates/aingle_ingest/src/lib.rs +++ b/crates/aingle_ingest/src/lib.rs @@ -155,7 +155,10 @@ mod tests { .iter() .any(|t| t.predicate == "status" && t.object == ObjectValue::Text("done".into()))); assert_eq!( - ex.triples.iter().filter(|t| t.predicate == "status").count(), + ex.triples + .iter() + .filter(|t| t.predicate == "status") + .count(), 2, "exactly the two real tasks become task nodes" ); @@ -180,7 +183,10 @@ mod tests { // A `🔁 every …` task records its recurrence as a `recur` fact on the // same stable task node (encoded compactly, e.g. `1m`), so MCP/queries // can see the repeat cadence. The reschedule itself is app-side. - let ex = extract("todos.md", "- [ ] rent \u{1F4C5} 2026-08-01 \u{1F501} every month\n"); + let ex = extract( + "todos.md", + "- [ ] rent \u{1F4C5} 2026-08-01 \u{1F501} every month\n", + ); let recur = ex .triples .iter() @@ -206,7 +212,10 @@ mod tests { .find(|t| t.predicate == "is_a" && t.object == ObjectValue::Text("card".into())) .expect("card is_a triple"); let subj = isa.subject.clone(); - assert_eq!(subj, "card:deck.md#cafef00dcafe", "id= in comment is sticky"); + assert_eq!( + subj, "card:deck.md#cafef00dcafe", + "id= in comment is sticky" + ); let has = |p: &str, o: ObjectValue| { ex.triples .iter() @@ -293,7 +302,10 @@ mod tests { - [ ] another real task\n"; let ex = extract("doc.md", md); assert_eq!( - ex.triples.iter().filter(|t| t.predicate == "status").count(), + ex.triples + .iter() + .filter(|t| t.predicate == "status") + .count(), 2, "only the two real tasks become task nodes" ); @@ -305,9 +317,8 @@ mod tests { .triples .iter() .any(|t| t.predicate == "tagged" && t.object == ObjectValue::Text("notatag".into()))); - assert!(!ex.triples.iter().any( - |t| t.predicate == "has_section" && t.object == ObjectValue::Text("not a heading".into()) - )); + assert!(!ex.triples.iter().any(|t| t.predicate == "has_section" + && t.object == ObjectValue::Text("not a heading".into()))); // The genuine heading outside the fence survives. assert!(ex .triples @@ -326,7 +337,11 @@ mod tests { .filter(|t| t.predicate == "status") .map(|t| t.subject.clone()) .collect(); - assert_eq!(subjects.len(), 2, "identical task text must not collapse to one node"); + assert_eq!( + subjects.len(), + 2, + "identical task text must not collapse to one node" + ); assert!(ex .triples .iter() @@ -340,7 +355,10 @@ mod tests { #[test] fn task_lines_still_yield_note_links_and_tags() { // A task line's wikilinks/tags must still attach to the note itself. - let ex = extract("todos.md", "- [ ] Follow up on [[sled]] about #durability\n"); + let ex = extract( + "todos.md", + "- [ ] Follow up on [[sled]] about #durability\n", + ); assert!(ex.triples.iter().any(|t| t.subject == "todos.md" && t.predicate == "links_to" && t.object == ObjectValue::Node("sled".into()))); diff --git a/crates/aingle_ingest/src/tasks.rs b/crates/aingle_ingest/src/tasks.rs index af7bf0de..7763da26 100644 --- a/crates/aingle_ingest/src/tasks.rs +++ b/crates/aingle_ingest/src/tasks.rs @@ -123,7 +123,11 @@ pub fn parse_task(line: &str) -> Option { .filter(|d| valid_date(d)); // Recurrence: encode `every ` as `` (e.g. `3d`). let recur = RECUR.captures(&rest).map(|c| { - let n: u32 = c.get(1).and_then(|m| m.as_str().parse().ok()).unwrap_or(1).max(1); + let n: u32 = c + .get(1) + .and_then(|m| m.as_str().parse().ok()) + .unwrap_or(1) + .max(1); let unit = match c[2].to_ascii_lowercase().as_str() { "day" => 'd', "week" => 'w', @@ -227,9 +231,15 @@ mod tests { assert_eq!(q.text, "standup"); // Week / year encodings; `every` optional. - assert_eq!(t("- [ ] x \u{1F501} every week").recur.as_deref(), Some("1w")); + assert_eq!( + t("- [ ] x \u{1F501} every week").recur.as_deref(), + Some("1w") + ); assert_eq!(t("- [ ] x \u{1F501} 2 weeks").recur.as_deref(), Some("2w")); - assert_eq!(t("- [ ] x \u{1F501} every 5 years").recur.as_deref(), Some("5y")); + assert_eq!( + t("- [ ] x \u{1F501} every 5 years").recur.as_deref(), + Some("5y") + ); // No recurrence → None. assert_eq!(t("- [ ] plain task").recur, None); @@ -249,7 +259,11 @@ mod tests { // visible in the text (nothing silently lost). let p = t("- [ ] file taxes \u{1F4C5} 2026-13-45"); assert_eq!(p.deadline, None); - assert!(p.text.contains("2026-13-45"), "invalid date stays in text: {}", p.text); + assert!( + p.text.contains("2026-13-45"), + "invalid date stays in text: {}", + p.text + ); // A valid date still parses and is stripped. let q = t("- [ ] pay \u{1F4C5} 2026-02-15 \u{23F3} 2026-02-01"); assert_eq!(q.deadline.as_deref(), Some("2026-02-15")); diff --git a/crates/aingle_logic/src/engine.rs b/crates/aingle_logic/src/engine.rs index 1b2c7cfc..2ed47b75 100644 --- a/crates/aingle_logic/src/engine.rs +++ b/crates/aingle_logic/src/engine.rs @@ -348,7 +348,7 @@ impl RuleEngine { /// * `bindings` - Mutable `Bindings` to accumulate variable assignments. /// * `depth` - Current recursion depth to prevent stack overflow and enforce `max_depth`. /// * `visited` - A `HashSet` to keep track of goals already visited in the current proof path - /// to detect and prevent inference loops. + /// to detect and prevent inference loops. /// * `result` - The mutable `BackwardChainResult` to record proof steps. /// /// # Returns diff --git a/crates/aingle_logic/src/rule.rs b/crates/aingle_logic/src/rule.rs index e89b7f07..f01c8a26 100644 --- a/crates/aingle_logic/src/rule.rs +++ b/crates/aingle_logic/src/rule.rs @@ -6,6 +6,8 @@ //! Rules are the fundamental building blocks of logical validation. //! They define conditions that must be met and actions to take. +use std::cmp::Reverse; + use aingle_graph::{NodeId, Predicate, Triple, Value}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -623,7 +625,7 @@ impl RuleSet { /// Get all enabled rules sorted by priority pub fn enabled_sorted(&self) -> Vec<&Rule> { let mut rules: Vec<_> = self.rules.iter().filter(|r| r.enabled).collect(); - rules.sort_by(|a, b| b.priority.cmp(&a.priority)); + rules.sort_by_key(|r| Reverse(r.priority)); rules } diff --git a/crates/aingle_minimal/tests/e2e_multi_node_tests.rs b/crates/aingle_minimal/tests/e2e_multi_node_tests.rs index ef5af902..2861ac23 100644 --- a/crates/aingle_minimal/tests/e2e_multi_node_tests.rs +++ b/crates/aingle_minimal/tests/e2e_multi_node_tests.rs @@ -662,15 +662,15 @@ fn test_ring_topology_simulation() { let mut nodes_with_peers: Vec<_> = nodes.into_iter().collect(); // Add peers in ring fashion - for i in 0..RING_SIZE { + for (i, node) in nodes_with_peers.iter_mut().enumerate() { let prev = if i == 0 { RING_SIZE - 1 } else { i - 1 }; let next = (i + 1) % RING_SIZE; let prev_addr: SocketAddr = format!("192.168.1.{}:5683", prev + 1).parse().unwrap(); let next_addr: SocketAddr = format!("192.168.1.{}:5683", next + 1).parse().unwrap(); - nodes_with_peers[i].add_peer(prev_addr); - nodes_with_peers[i].add_peer(next_addr); + node.add_peer(prev_addr); + node.add_peer(next_addr); } // Verify each node has exactly 2 peers diff --git a/crates/aingle_viz/src/dag.rs b/crates/aingle_viz/src/dag.rs index 9f4a1685..65dd7071 100644 --- a/crates/aingle_viz/src/dag.rs +++ b/crates/aingle_viz/src/dag.rs @@ -45,6 +45,8 @@ //! The [`DagView::to_d3_json`] method converts the DAG to a format compatible with //! D3.js force-directed graphs, making it easy to visualize in a web browser. +use std::cmp::Reverse; + use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -662,7 +664,7 @@ impl DagView { /// ``` pub fn recent_nodes(&self, limit: usize) -> Vec<&DagNode> { let mut nodes: Vec<_> = self.nodes.iter().collect(); - nodes.sort_by(|a, b| b.timestamp.cmp(&a.timestamp)); + nodes.sort_by_key(|n| Reverse(n.timestamp)); nodes.into_iter().take(limit).collect() } From 487f365ade13c471c79d0e71084f52911d114472 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 12:04:44 +0200 Subject: [PATCH 05/10] ci: run the checks on every pull request, not just those onto main and dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `pull_request: branches: [main, dev]` meant a PR onto any other branch ran no checks at all. A stack of dependent PRs therefore had no CI until each was retargeted — which is after the review, not before it. Found by building such a stack: three of its five PRs showed no checks whatsoever, and nothing about the interface said why. --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8158b9f2..a63f4e72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,11 @@ name: AIngle CI on: push: branches: [ main, dev ] + # No branch filter on pull_request, on purpose. Filtering to [main, dev] meant + # a PR onto any other branch ran NO checks at all — so a stack of dependent + # PRs was invisible to CI until each one was retargeted, which is exactly when + # it is too late to learn something. Every PR gets the same gate. pull_request: - branches: [ main, dev ] env: CARGO_TERM_COLOR: always From 2ca262fd2ce74dfdeda46733acbccdde14c33f4a Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 12:23:48 +0200 Subject: [PATCH 06/10] fix(ingest): rewrite a fallthrough with `?`, as clippy 1.97 asks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `clippy::question_mark` is new in 1.97. Nobody touched this code; the toolchain moved under it. Which is the whole argument for pinning: this repository installs `dtolnay/rust-toolchain@stable`, so the gate added in this PR can go red from a compiler release with no commit behind it. Verified by installing 1.97.0 locally — clippy, rustfmt and the tests are all clean under the exact toolchain the runner used, which 1.96 could not have told me. --- crates/aingle_ingest/src/tasks.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/aingle_ingest/src/tasks.rs b/crates/aingle_ingest/src/tasks.rs index 7763da26..e571a1ec 100644 --- a/crates/aingle_ingest/src/tasks.rs +++ b/crates/aingle_ingest/src/tasks.rs @@ -106,10 +106,9 @@ pub fn parse_task(line: &str) -> Option { let ch = c[1].chars().next().unwrap(); let rest = c.get(2).map_or(String::new(), |m| m.as_str().to_string()); (status_from_checkbox(ch), rest) - } else if let Some(c) = KEYWORD.captures(line) { - (status_from_keyword(&c[1]), c[2].to_string()) } else { - return None; + let c = KEYWORD.captures(line)?; + (status_from_keyword(&c[1]), c[2].to_string()) }; let priority = PRIORITY.captures(&rest).and_then(|c| c[1].chars().next()); From 98fa4427f1131c1f0f912f7fa9758d8afb8ae0ce Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 11:51:04 +0200 Subject: [PATCH 07/10] fix(ai): stop a single NaN from silently disabling two subsystems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both sites carried a clamp-shaped expression that clippy wanted rewritten. Following that advice mechanically would have introduced a bug in one and papered over a worse one in the other, so each is settled here instead. SurpriseGate. `min`/`max` discard NaN and return the other operand; `clamp` propagates it. But the real entry point is upstream: `record_surprise` feeds Welford's algorithm, and one non-finite sample makes the running mean NaN permanently — every later update keeps it, the adaptive threshold inherits it, and because every comparison against NaN is false the gate stops firing with nothing to show for it. Reject non-finite samples at the boundary, and fall back to the CONFIGURED threshold — the non-adaptive baseline this type already has — rather than to an invented constant, if unusable statistics arrive some other way (deserialised state is not covered by the entry guard). TransactionClassifier::confidence. `partial_cmp().unwrap()` panics on a NaN distance, which `l2_distance` produces from a corrupt centroid, so the process died inside a confidence calculation. `total_cmp` is total and also sorts NaN last, so a corrupt centroid can no longer pass itself off as the nearest one. And with every distance unusable the old code fell past the `second_dist > 0.0` test to return 1.0 — FULL confidence at the exact moment there is no information. It now answers 0.5, the same neutral value it already returns when there are no centroids at all. Five tests, each pinning a failure that was silent: a poisoned mean, a NaN threshold, the panic, and full confidence from no information. --- crates/aingle_ai/src/ineru/surprise_gate.rs | 89 +++++++++++++++---- .../src/nested_learning/transaction_level.rs | 60 ++++++++++--- 2 files changed, 121 insertions(+), 28 deletions(-) diff --git a/crates/aingle_ai/src/ineru/surprise_gate.rs b/crates/aingle_ai/src/ineru/surprise_gate.rs index 677eccd0..f459e7a3 100644 --- a/crates/aingle_ai/src/ineru/surprise_gate.rs +++ b/crates/aingle_ai/src/ineru/surprise_gate.rs @@ -75,7 +75,17 @@ impl SurpriseGate { } /// Record a surprise value for calibration + /// + /// Non-finite samples are dropped. Welford's algorithm has no way back from + /// one: `mean += delta / n` with a NaN delta makes the mean NaN, every + /// later update keeps it NaN, and `adaptive_threshold` inherits it. A NaN + /// threshold is worse than any number, because every comparison against it + /// is false — the gate stops firing and nothing says so. pub fn record_surprise(&mut self, surprise: f32) { + if !surprise.is_finite() { + return; + } + // Add to recent window self.recent_surprises.push_back(surprise); if self.recent_surprises.len() > self.calibration_window { @@ -131,24 +141,22 @@ impl SurpriseGate { /// Get adaptive threshold based on recent statistics /// - /// NOT `clamp`, and not by accident. `f32::min(NaN, 1.0)` returns `1.0` — - /// `min`/`max` discard NaN and return the other operand — so a NaN mean or - /// std yields a threshold of `1.0` here. `NaN.clamp(0.1, 1.0)` returns NaN, - /// and a NaN threshold is worse than either bound: every comparison against - /// it is false, so the gate would stop firing and do so silently. - /// `get_std()` can produce NaN from an empty history or from a negative - /// variance caused by floating-point error. - /// - /// The right fix is neither expression: branch on `is_nan()` explicitly and - /// pin the chosen fallback with a test. Which fallback is correct — today's - /// 1.0, meaning the gate almost never fires, or 0.1, meaning it almost - /// always does — belongs to whoever owns this gate, so it is left as a - /// decision rather than made silently by a lint fix. - #[allow(clippy::manual_clamp)] + /// Falls back to the configured [`Self::set_threshold`] value when the + /// running statistics cannot produce a usable number. `record_surprise` + /// already refuses non-finite samples, so this should be unreachable — but + /// it is the difference between a wrong threshold and a NaN one, and a NaN + /// threshold disables the gate silently. Deserialised state is not covered + /// by the entry guard either. pub fn adaptive_threshold(&self) -> f32 { // Use mean + 1 std as adaptive threshold let std = self.get_std(); - (self.mean_surprise + std).min(1.0).max(0.1) + let adaptive = self.mean_surprise + std; + + if !adaptive.is_finite() { + return self.threshold.clamp(0.1, 1.0); + } + + adaptive.clamp(0.1, 1.0) } /// Get statistics @@ -255,4 +263,55 @@ mod tests { let adaptive = gate.adaptive_threshold(); assert!(adaptive > 0.0 && adaptive < 1.0); } + + #[test] + fn a_non_finite_sample_cannot_poison_the_running_statistics() { + // Welford has no way back from a NaN: `mean += delta / n` with a NaN + // delta makes the mean NaN and every later update keeps it that way. + let mut gate = SurpriseGate::new(0.5); + for _ in 0..20 { + gate.record_surprise(0.4); + } + let before = gate.adaptive_threshold(); + + gate.record_surprise(f32::NAN); + gate.record_surprise(f32::INFINITY); + gate.record_surprise(f32::NEG_INFINITY); + + assert_eq!( + gate.adaptive_threshold(), + before, + "a non-finite sample must be dropped, not folded into the mean" + ); + } + + #[test] + fn the_adaptive_threshold_is_always_a_usable_number() { + // The failure this guards is silent: every comparison against a NaN + // threshold is false, so `should_update` would stop firing and the + // memory would quietly stop learning. + let mut gate = SurpriseGate::new(0.5); + gate.record_surprise(f32::NAN); + for s in [0.0, 1.0, 0.5, f32::NAN, 0.9] { + gate.record_surprise(s); + } + + let t = gate.adaptive_threshold(); + assert!(t.is_finite(), "threshold was {t}"); + assert!( + (0.1..=1.0).contains(&t), + "threshold {t} left its documented range" + ); + } + + #[test] + fn unusable_statistics_fall_back_to_the_configured_threshold() { + // Not to an invented constant: the value the caller set is the + // non-adaptive baseline this gate already has. + let mut gate = SurpriseGate::new(0.5); + gate.set_threshold(0.7); + gate.mean_surprise = f32::NAN; + + assert!((gate.adaptive_threshold() - 0.7).abs() < 1e-6); + } } diff --git a/crates/aingle_ai/src/nested_learning/transaction_level.rs b/crates/aingle_ai/src/nested_learning/transaction_level.rs index 19d8350d..7a85a251 100644 --- a/crates/aingle_ai/src/nested_learning/transaction_level.rs +++ b/crates/aingle_ai/src/nested_learning/transaction_level.rs @@ -191,25 +191,29 @@ impl TransactionClassifier { return 0.5; } - // Find min and second min distance + // Find min and second min distance. + // + // `total_cmp`, not `partial_cmp().unwrap()`: the latter PANICS on a NaN + // distance, which `l2_distance` yields from a centroid holding one. A + // total order also sorts NaN last, so a corrupt centroid cannot pass + // itself off as the nearest one. let mut sorted = distances.clone(); - sorted.sort_by(|a, b| a.partial_cmp(b).unwrap()); + sorted.sort_by(f32::total_cmp); let min_dist = sorted.first().copied().unwrap_or(1.0); let second_dist = sorted.get(1).copied().unwrap_or(min_dist); - // Confidence based on separation. - // - // NOT `clamp`: `max`/`min` discard NaN and `clamp` propagates it, so the - // two differ exactly when the input is NaN. Here the division is guarded - // by `second_dist > 0.0`, so NaN should be unreachable — but that rests - // on the `partial_cmp().unwrap()` in the sort above, which PANICS on a - // NaN distance rather than producing one. Substituting `clamp` would be - // safe and would also settle nothing; the real work is making that sort - // total (`total_cmp`) and then deciding what a NaN distance means. - #[allow(clippy::manual_clamp)] + // Nothing usable to measure a separation with. Answer what this function + // already answers when there are no centroids at all, rather than let + // the `> 0.0` test below fall through to 1.0 — which would report FULL + // confidence precisely when there is no information. + if !min_dist.is_finite() || !second_dist.is_finite() { + return 0.5; + } + + // Confidence based on separation if second_dist > 0.0 { - (1.0 - min_dist / second_dist).max(0.0).min(1.0) + (1.0 - min_dist / second_dist).clamp(0.0, 1.0) } else { 1.0 } @@ -316,4 +320,34 @@ mod tests { let processed2 = tl.process(tx); assert!(processed2.confidence > 0.0); } + + #[test] + fn a_nan_centroid_does_not_panic_and_does_not_win() { + // `partial_cmp().unwrap()` panicked here on any NaN distance, taking the + // process down from inside a confidence calculation. + let mut classifier = TransactionClassifier::new(); + classifier.centroids.clear(); + classifier.centroids.insert("good".into(), vec![0.0; 16]); + classifier + .centroids + .insert("corrupt".into(), vec![f32::NAN; 16]); + + let c = classifier.confidence(&[0.5; 16]); + + assert!(c.is_finite(), "confidence was {c}"); + assert!((0.0..=1.0).contains(&c), "confidence {c} left its range"); + } + + #[test] + fn no_measurable_separation_reports_neutral_confidence_not_full() { + // With every distance unusable the old code fell through the + // `second_dist > 0.0` test and returned 1.0 — maximum confidence at the + // exact moment there was no information at all. + let mut classifier = TransactionClassifier::new(); + classifier.centroids.clear(); + classifier.centroids.insert("a".into(), vec![f32::NAN; 16]); + classifier.centroids.insert("b".into(), vec![f32::NAN; 16]); + + assert_eq!(classifier.confidence(&[0.5; 16]), 0.5); + } } From 1ca5a9287e7350048caace59f0efc899d9cc9a6c Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 12:00:19 +0200 Subject: [PATCH 08/10] refactor(dag): ExportFormat implements FromStr instead of shadowing it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `ExportFormat::from_str` was an inherent method returning `Option`, which shadows `std::str::FromStr::from_str` and returns a different shape from it. A caller reading `ExportFormat::from_str(..)` could not tell which one they were getting, and got no reason for the failure either way. Implement the trait with an error that carries the offending input and knows the accepted spellings. The list of valid formats lived in the REST handler, where it could drift away from the parser with nothing to catch it — it already advertised "dot, mermaid, json" while the parser also accepted "graphviz" and "md". Now the type owns it and a test asserts the message names every spelling the parser takes. One caller migrated. `cargo clippy --workspace --all-targets -- -D warnings` and `cargo fmt --all --check` are both clean with no `#[allow]` left anywhere from this series. --- crates/aingle_cortex/src/rest/dag.rs | 10 ++-- crates/aingle_graph/src/dag/export.rs | 78 ++++++++++++++++++--------- crates/aingle_graph/src/dag/mod.rs | 2 +- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/crates/aingle_cortex/src/rest/dag.rs b/crates/aingle_cortex/src/rest/dag.rs index 0bdd3048..6afcc203 100644 --- a/crates/aingle_cortex/src/rest/dag.rs +++ b/crates/aingle_cortex/src/rest/dag.rs @@ -387,12 +387,10 @@ pub async fn get_dag_export( ) -> Result { use axum::response::IntoResponse; - let format = aingle_graph::dag::ExportFormat::from_str(&query.format).ok_or_else(|| { - Error::InvalidInput(format!( - "Unknown format '{}'. Use: dot, mermaid, json", - query.format - )) - })?; + let format: aingle_graph::dag::ExportFormat = query + .format + .parse() + .map_err(|e: aingle_graph::dag::UnknownExportFormat| Error::InvalidInput(e.to_string()))?; let graph = state.graph.read().await; let dag_graph = graph diff --git a/crates/aingle_graph/src/dag/export.rs b/crates/aingle_graph/src/dag/export.rs index 11b02bc2..349ede5d 100644 --- a/crates/aingle_graph/src/dag/export.rs +++ b/crates/aingle_graph/src/dag/export.rs @@ -40,24 +40,41 @@ pub enum ExportFormat { Json, } -impl ExportFormat { - /// Parse from string (case-insensitive). - /// - /// Shadows `std::str::FromStr::from_str`, which clippy rightly flags: a - /// caller writing `ExportFormat::from_str` cannot tell which one they get, - /// and this one returns `Option` where the trait returns `Result`. - /// - /// The fix is to implement `FromStr` properly with an error type and drop - /// this inherent method. It is cheap — there is exactly one caller outside - /// this module's own tests — but it changes a public signature, so it is a - /// deliberate change with its own review and not a line in a lint sweep. - #[allow(clippy::should_implement_trait)] - pub fn from_str(s: &str) -> Option { +/// The string offered to [`ExportFormat`]'s `FromStr` named no known format. +/// +/// Carries the offending input and knows the accepted spellings, so a caller +/// does not have to keep its own copy of the list — which is how a REST handler +/// ends up advertising formats the parser no longer accepts. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct UnknownExportFormat(pub String); + +impl std::fmt::Display for UnknownExportFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "unknown export format '{}'. Use: dot (or graphviz), mermaid (or md), json", + self.0 + ) + } +} + +impl std::error::Error for UnknownExportFormat {} + +/// Parse from string (case-insensitive). +/// +/// This was an inherent `from_str` returning `Option`, which shadowed +/// `std::str::FromStr::from_str` and returned a different shape from it: a +/// caller reading `ExportFormat::from_str(..)` could not tell which one they +/// were getting, and got no reason for the failure either way. +impl std::str::FromStr for ExportFormat { + type Err = UnknownExportFormat; + + fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { - "dot" | "graphviz" => Some(Self::Dot), - "mermaid" | "md" => Some(Self::Mermaid), - "json" => Some(Self::Json), - _ => None, + "dot" | "graphviz" => Ok(Self::Dot), + "mermaid" | "md" => Ok(Self::Mermaid), + "json" => Ok(Self::Json), + _ => Err(UnknownExportFormat(s.to_string())), } } } @@ -319,15 +336,28 @@ mod tests { #[test] fn test_export_format_parsing() { - assert_eq!(ExportFormat::from_str("dot"), Some(ExportFormat::Dot)); - assert_eq!(ExportFormat::from_str("DOT"), Some(ExportFormat::Dot)); - assert_eq!(ExportFormat::from_str("graphviz"), Some(ExportFormat::Dot)); + assert_eq!("dot".parse(), Ok(ExportFormat::Dot)); + assert_eq!("DOT".parse(), Ok(ExportFormat::Dot)); + assert_eq!("graphviz".parse(), Ok(ExportFormat::Dot)); + assert_eq!("mermaid".parse(), Ok(ExportFormat::Mermaid)); + assert_eq!("md".parse(), Ok(ExportFormat::Mermaid)); + assert_eq!("json".parse(), Ok(ExportFormat::Json)); assert_eq!( - ExportFormat::from_str("mermaid"), - Some(ExportFormat::Mermaid) + "xml".parse::(), + Err(UnknownExportFormat("xml".to_string())) ); - assert_eq!(ExportFormat::from_str("json"), Some(ExportFormat::Json)); - assert_eq!(ExportFormat::from_str("xml"), None); + } + + #[test] + fn an_unknown_format_says_what_it_would_have_accepted() { + // The list used to live in the REST handler, where it could drift away + // from the parser without anything noticing. + let e = "xml".parse::().unwrap_err(); + let msg = e.to_string(); + assert!(msg.contains("xml"), "{msg}"); + for accepted in ["dot", "graphviz", "mermaid", "md", "json"] { + assert!(msg.contains(accepted), "{accepted} missing from: {msg}"); + } } #[test] diff --git a/crates/aingle_graph/src/dag/mod.rs b/crates/aingle_graph/src/dag/mod.rs index 970d8ee9..5a03056e 100644 --- a/crates/aingle_graph/src/dag/mod.rs +++ b/crates/aingle_graph/src/dag/mod.rs @@ -33,7 +33,7 @@ pub use action::{ pub use backend::SledDagBackend; pub use backend::{DagBackend, MemoryDagBackend}; pub use canonical::{CanonicalAction, CANONICAL_SPEC, HASH_ALG, SIGNATURE_ALG, SIGNED_MESSAGE}; -pub use export::{DagGraph, ExportFormat}; +pub use export::{DagGraph, ExportFormat, UnknownExportFormat}; pub use pruning::{PruneResult, RetentionPolicy}; #[cfg(feature = "dag-sign")] pub use signing::{DagSigningKey, DagVerifyingKey, VerifyResult}; From 350ea5c3892436f5fe8d9ec47519af1e748aeff9 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Wed, 29 Jul 2026 13:10:45 +0200 Subject: [PATCH 09/10] chore: pin the toolchain and declare an MSRV that is true MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two blockers for tagging a release, both of the same shape as the lint gate: a number nothing verified. PIN. The workflows installed `dtolnay/rust-toolchain@stable`, so on 2026-07-29 the runner moved to clippy 1.97, `question_mark` arrived, and CI went red with no commit behind it. Sixteen installations across four workflows now take `RUST_VERSION`, declared once. Not a `rust-toolchain.toml` alone: that action exports RUSTUP_TOOLCHAIN, which takes precedence over the file, so the pin would be ignored exactly where it is needed. The file is here too, for local parity, and the note says they must move together. MSRV. Twelve crates said 1.83, the CI environment said 1.85, CLAUDE.md said 1.83, and the truth was none of them. Bisected: 1.85 fails (`cranelift` needs 1.90), 1.90 fails (`wasmer` needs 1.91), 1.91 builds. Nothing had ever noticed because the MSRV job ran `cargo check -p aingle_minimal` — the single member with no wasmer in its tree. It now checks the whole workspace, and the figure lives once in `[workspace.package]` with thirteen members inheriting it. Uniform rather than per-crate deliberately: a lower number on a crate never built at that version is the same unverified promise being removed here. Raising the declared MSRV also lets clippy suggest APIs that were too new before, which surfaced eight `manual_is_multiple_of` sites. Applied. Verified under both toolchains that now matter: clippy and rustfmt clean and 2248 tests green on the pinned 1.97.0, and `cargo check --workspace --all-targets` clean on the declared 1.91.0. --- .github/workflows/ci.yml | 61 ++++++++++++++++---- .github/workflows/docs.yml | 6 +- .github/workflows/release.yml | 9 ++- .github/workflows/security.yml | 14 ++++- Cargo.toml | 14 +++++ crates/aingle_ai/Cargo.toml | 3 +- crates/aingle_ai/src/nested_learning/mod.rs | 10 +++- crates/aingle_contracts/Cargo.toml | 3 +- crates/aingle_cortex/Cargo.toml | 3 +- crates/aingle_cortex/src/service/validate.rs | 2 +- crates/aingle_graph/Cargo.toml | 3 +- crates/aingle_ingest/Cargo.toml | 3 +- crates/aingle_logic/Cargo.toml | 3 +- crates/aingle_minimal/Cargo.toml | 3 +- crates/aingle_minimal/src/node.rs | 2 +- crates/aingle_minimal/src/ota.rs | 2 +- crates/aingle_minimal/src/types.rs | 2 +- crates/aingle_raft/Cargo.toml | 3 +- crates/aingle_viz/Cargo.toml | 3 +- crates/aingle_wal/Cargo.toml | 3 +- crates/aingle_zk/Cargo.toml | 3 +- crates/aingle_zk/src/merkle.rs | 2 +- crates/ineru/Cargo.toml | 3 +- crates/ineru/benches/memory_bench.rs | 2 +- crates/kaneru/Cargo.toml | 3 +- crates/kaneru/src/kaneru_agent.rs | 2 +- rust-toolchain.toml | 10 ++++ 27 files changed, 124 insertions(+), 53 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a63f4e72..aac324cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,9 +10,26 @@ on: pull_request: env: + # PINNED, not `stable`. On 2026-07-29 the runner moved to clippy 1.97, which + # added `question_mark`, and the lint gate went red with no commit behind it — + # a compiler release is not a change to this repository and must not be able + # to fail its CI. + # + # Pinning here rather than in a `rust-toolchain.toml`: the toolchain action + # exports RUSTUP_TOOLCHAIN, which takes precedence over that file, so the + # pin would be ignored in exactly the place it is needed. The file exists too, + # for local parity. + # + # To bump: raise this, run `cargo + clippy --workspace --all-targets -- + # -D warnings` and `cargo + fmt --all -- --check` locally first, and fix + # whatever the new release added in the same commit. + RUST_VERSION: "1.97.0" CARGO_TERM_COLOR: always RUST_BACKTRACE: 1 - MSRV: "1.85" + # The version this project promises to build on, verified by the job below + # against the WHOLE workspace. Keep in step with `rust-version` in the root + # Cargo.toml, which is where consumers read it. + MSRV: "1.91" jobs: # MSRV (Minimum Supported Rust Version) check @@ -43,8 +60,11 @@ jobs: restore-keys: | ${{ runner.os }}-cargo-msrv- + # `-p aingle_minimal` before, which is the one member with no wasmer in + # its dependency tree — so it passed at 1.85 while the workspace could + # not build below 1.91. A promise nothing checks is not a promise. - name: Check MSRV - run: cargo check -p aingle_minimal + run: cargo check --workspace --all-targets # Format check fmt: @@ -54,8 +74,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ env.RUST_VERSION }} components: rustfmt - name: Check formatting @@ -69,8 +90,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ env.RUST_VERSION }} components: clippy - name: Install system dependencies @@ -118,7 +140,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | @@ -151,7 +175,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | @@ -183,7 +209,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | @@ -227,7 +255,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install cargo-audit run: cargo install cargo-audit @@ -245,8 +275,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ env.RUST_VERSION }} components: llvm-tools-preview - name: Install system dependencies @@ -288,7 +319,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | @@ -327,7 +360,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | @@ -356,7 +391,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3a83cbcb..63c391cc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -15,6 +15,8 @@ concurrency: cancel-in-progress: true env: + # Pinned on purpose: see the note in ci.yml. + RUST_VERSION: "1.97.0" CARGO_TERM_COLOR: always jobs: @@ -25,7 +27,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6c64d3c..bd5c0652 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,6 +15,8 @@ permissions: contents: write env: + # Pinned on purpose: see the note in ci.yml. + RUST_VERSION: "1.97.0" CARGO_TERM_COLOR: always jobs: @@ -50,8 +52,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ env.RUST_VERSION }} targets: ${{ matrix.target }} - name: Install system dependencies (Linux) @@ -163,7 +166,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install system dependencies run: | diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index bd585f82..8fb37bf7 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -17,6 +17,8 @@ on: workflow_dispatch: env: + # Pinned on purpose: see the note in ci.yml. + RUST_VERSION: "1.97.0" CARGO_TERM_COLOR: always jobs: @@ -28,7 +30,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install cargo-audit run: cargo install cargo-audit @@ -60,7 +64,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install cargo-deny run: cargo install cargo-deny @@ -109,7 +115,9 @@ jobs: - uses: actions/checkout@v6 - name: Install Rust - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_VERSION }} - name: Install cargo-vet run: cargo install cargo-vet diff --git a/Cargo.toml b/Cargo.toml index 3c691e85..da696937 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,17 @@ +[workspace.package] +# The MSRV, declared ONCE. It said 1.83 in twelve crates, 1.85 in the CI +# environment and 1.83 in CLAUDE.md, and the truth was none of those: the +# workspace needs 1.91, because `wasmer` (via aingle_contracts) requires it +# and `cranelift` under it requires 1.90. Verified by bisection — 1.85 and +# 1.90 both fail, 1.91 builds. Nothing had ever checked, because the MSRV job +# ran `cargo check -p aingle_minimal`: the one member with no wasmer in its +# tree. +# +# Uniform rather than per-crate on purpose: a lower number on a crate that +# has not been built at that version is the same unverified promise this is +# replacing. Refine per crate only with a run behind each figure. +rust-version = "1.91" + [workspace] resolver = "2" members = [ diff --git a/crates/aingle_ai/Cargo.toml b/crates/aingle_ai/Cargo.toml index 6f2e2d10..2dcf34df 100644 --- a/crates/aingle_ai/Cargo.toml +++ b/crates/aingle_ai/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "ai", "titans", "nested-learning", "memory"] categories = ["science", "algorithms"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [dependencies] # Serialization serde = { version = "1", features = ["derive"] } diff --git a/crates/aingle_ai/src/nested_learning/mod.rs b/crates/aingle_ai/src/nested_learning/mod.rs index 7021b764..352050df 100644 --- a/crates/aingle_ai/src/nested_learning/mod.rs +++ b/crates/aingle_ai/src/nested_learning/mod.rs @@ -93,7 +93,10 @@ impl NestedLearning { self.tx_count += 1; // 4. Check if optimizer-level update is needed - if self.tx_count % self.config.optimizer_update_interval == 0 { + if self + .tx_count + .is_multiple_of(self.config.optimizer_update_interval) + { debug!( tx_count = self.tx_count, "Triggering optimizer-level update" @@ -114,7 +117,10 @@ impl NestedLearning { self.block_count += 1; // Check if meta-level update is needed - if self.block_count % self.config.meta_update_interval == 0 { + if self + .block_count + .is_multiple_of(self.config.meta_update_interval) + { debug!( block_count = self.block_count, "Triggering meta-level update" diff --git a/crates/aingle_contracts/Cargo.toml b/crates/aingle_contracts/Cargo.toml index 605b2cd6..71f74d5b 100644 --- a/crates/aingle_contracts/Cargo.toml +++ b/crates/aingle_contracts/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "smart-contracts", "wasm", "dsl", "blockchain"] categories = ["wasm", "database"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = ["runtime"] runtime = ["dep:wasmer"] diff --git a/crates/aingle_cortex/Cargo.toml b/crates/aingle_cortex/Cargo.toml index a0529c0e..02f1a776 100644 --- a/crates/aingle_cortex/Cargo.toml +++ b/crates/aingle_cortex/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "api", "graphql", "sparql", "rest"] categories = ["web-programming", "database"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = ["rest", "sparql", "auth", "dag"] rest = [] diff --git a/crates/aingle_cortex/src/service/validate.rs b/crates/aingle_cortex/src/service/validate.rs index a98cfff7..bc76afbf 100644 --- a/crates/aingle_cortex/src/service/validate.rs +++ b/crates/aingle_cortex/src/service/validate.rs @@ -262,7 +262,7 @@ mod tests { let s = v .as_str() .unwrap_or_else(|| panic!("expected hex, got {v}")); - assert!(s.len() % 2 == 0, "hex must be byte-aligned: {s:?}"); + assert!(s.len().is_multiple_of(2), "hex must be byte-aligned: {s:?}"); (0..s.len() / 2) .map(|i| u8::from_str_radix(&s[i * 2..i * 2 + 2], 16).expect("hex digit")) .collect() diff --git a/crates/aingle_graph/Cargo.toml b/crates/aingle_graph/Cargo.toml index 453ea2aa..83b48b23 100644 --- a/crates/aingle_graph/Cargo.toml +++ b/crates/aingle_graph/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "graph", "rdf", "semantic", "triplestore"] categories = ["database", "data-structures"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = ["sled-backend"] # Storage backends diff --git a/crates/aingle_ingest/Cargo.toml b/crates/aingle_ingest/Cargo.toml index 21b1e49b..2617c120 100644 --- a/crates/aingle_ingest/Cargo.toml +++ b/crates/aingle_ingest/Cargo.toml @@ -4,8 +4,7 @@ version = "0.8.0" description = "Structural extraction of triples and text chunks from markdown/code for AIngle" license = "Apache-2.0 OR LicenseRef-Commercial" edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [dependencies] aingle_graph = { version = "0.8", path = "../aingle_graph", features = ["dag"] } serde = { version = "1.0", features = ["derive"] } diff --git a/crates/aingle_logic/Cargo.toml b/crates/aingle_logic/Cargo.toml index d2d6a683..ce81182b 100644 --- a/crates/aingle_logic/Cargo.toml +++ b/crates/aingle_logic/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "logic", "reasoning", "validation", "rules"] categories = ["algorithms", "data-structures"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = [] # Enable SPARQL-like query rules diff --git a/crates/aingle_minimal/Cargo.toml b/crates/aingle_minimal/Cargo.toml index d59a0cb3..41f5d6f4 100644 --- a/crates/aingle_minimal/Cargo.toml +++ b/crates/aingle_minimal/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "iot", "dag", "embedded"] categories = ["embedded", "network-programming"] edition = "2021" -rust-version = "1.85" - +rust-version.workspace = true [features] default = ["std", "coap", "sqlite"] std = [] diff --git a/crates/aingle_minimal/src/node.rs b/crates/aingle_minimal/src/node.rs index 3bef02a1..3f89b76f 100644 --- a/crates/aingle_minimal/src/node.rs +++ b/crates/aingle_minimal/src/node.rs @@ -569,7 +569,7 @@ impl MinimalNode { while self.running.load(Ordering::SeqCst) { // Sync discovered peers periodically (every 100 iterations = ~1 second) discovery_sync_counter = discovery_sync_counter.wrapping_add(1); - if discovery_sync_counter % 100 == 0 { + if discovery_sync_counter.is_multiple_of(100) { self.network.sync_discovered_peers(); } diff --git a/crates/aingle_minimal/src/ota.rs b/crates/aingle_minimal/src/ota.rs index 01ac4c7b..af76b91e 100644 --- a/crates/aingle_minimal/src/ota.rs +++ b/crates/aingle_minimal/src/ota.rs @@ -558,7 +558,7 @@ mod hex { } pub fn decode(s: &str) -> Result, String> { - if s.len() % 2 != 0 { + if !s.len().is_multiple_of(2) { return Err("Invalid hex string length".to_string()); } diff --git a/crates/aingle_minimal/src/types.rs b/crates/aingle_minimal/src/types.rs index 5de73a15..78750b06 100644 --- a/crates/aingle_minimal/src/types.rs +++ b/crates/aingle_minimal/src/types.rs @@ -732,7 +732,7 @@ mod hex { } pub fn decode(s: &str) -> Result, FromHexError> { - if s.len() % 2 != 0 { + if !s.len().is_multiple_of(2) { return Err(FromHexError::OddLength); } (0..s.len()) diff --git a/crates/aingle_raft/Cargo.toml b/crates/aingle_raft/Cargo.toml index c197fb3b..afd50472 100644 --- a/crates/aingle_raft/Cargo.toml +++ b/crates/aingle_raft/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "raft", "consensus", "clustering"] categories = ["database"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = [] dag = ["aingle_graph/dag"] diff --git a/crates/aingle_viz/Cargo.toml b/crates/aingle_viz/Cargo.toml index 45e4f806..6bf1c6cf 100644 --- a/crates/aingle_viz/Cargo.toml +++ b/crates/aingle_viz/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "visualization", "dag", "graph"] categories = ["visualization", "web-programming"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = [] diff --git a/crates/aingle_wal/Cargo.toml b/crates/aingle_wal/Cargo.toml index 85b53903..6ad89fa8 100644 --- a/crates/aingle_wal/Cargo.toml +++ b/crates/aingle_wal/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "wal", "replication", "clustering"] categories = ["database"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [dependencies] serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/aingle_zk/Cargo.toml b/crates/aingle_zk/Cargo.toml index cdccfa20..918db1f2 100644 --- a/crates/aingle_zk/Cargo.toml +++ b/crates/aingle_zk/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "zkp", "zero-knowledge", "privacy", "cryptography"] categories = ["cryptography"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = ["std"] std = [] diff --git a/crates/aingle_zk/src/merkle.rs b/crates/aingle_zk/src/merkle.rs index f69eb957..ad22e6f3 100644 --- a/crates/aingle_zk/src/merkle.rs +++ b/crates/aingle_zk/src/merkle.rs @@ -136,7 +136,7 @@ impl MerkleTree { while level_size > 1 { // Sibling index - let sibling_index = if current_index % 2 == 0 { + let sibling_index = if current_index.is_multiple_of(2) { current_index + 1 } else { current_index - 1 diff --git a/crates/ineru/Cargo.toml b/crates/ineru/Cargo.toml index 850a50c4..cbfbffd4 100644 --- a/crates/ineru/Cargo.toml +++ b/crates/ineru/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "ai", "memory", "neural", "agents"] categories = ["science", "data-structures"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = ["std"] std = [] diff --git a/crates/ineru/benches/memory_bench.rs b/crates/ineru/benches/memory_bench.rs index 25157415..7c2d1a75 100644 --- a/crates/ineru/benches/memory_bench.rs +++ b/crates/ineru/benches/memory_bench.rs @@ -187,7 +187,7 @@ fn bench_iot_mode(c: &mut Criterion) { let recent = memory.recall_recent(5); // 3. Periodic maintenance (every 100 readings) - if counter % 100 == 0 { + if counter.is_multiple_of(100) { let _ = memory.decay(); } diff --git a/crates/kaneru/Cargo.toml b/crates/kaneru/Cargo.toml index 2bfb982f..25820958 100644 --- a/crates/kaneru/Cargo.toml +++ b/crates/kaneru/Cargo.toml @@ -10,8 +10,7 @@ authors = ["Apilium Technologies "] keywords = ["aingle", "ai", "agents", "rl", "iot"] categories = ["science", "network-programming"] edition = "2021" -rust-version = "1.83" - +rust-version.workspace = true [features] default = ["std"] std = [] diff --git a/crates/kaneru/src/kaneru_agent.rs b/crates/kaneru/src/kaneru_agent.rs index c948d3fb..d212174d 100644 --- a/crates/kaneru/src/kaneru_agent.rs +++ b/crates/kaneru/src/kaneru_agent.rs @@ -383,7 +383,7 @@ impl KaneruAgent { self.learning.add_experience(exp); // 5. Perform experience replay - if self.stats.total_steps % 10 == 0 { + if self.stats.total_steps.is_multiple_of(10) { self.learning.replay_batch(32, &self.available_actions); } diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..01d3e090 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,10 @@ +# Local parity with CI, which pins `RUST_VERSION` in .github/workflows/ci.yml — +# see the note there for why the pin lives in the workflow rather than only here. +# +# CI overrides this file (the toolchain action exports RUSTUP_TOOLCHAIN), so the +# two must be bumped together or a developer will verify against a version the +# runner does not use. That is how a lint gate goes red on a machine that +# reported it green. +[toolchain] +channel = "1.97.0" +components = ["clippy", "rustfmt"] From 4e8d02ee87631c781bbde5212bbb5967ad0478a9 Mon Sep 17 00:00:00 2001 From: ApiliumDevTeam Date: Thu, 30 Jul 2026 11:45:46 +0200 Subject: [PATCH 10/10] chore(release): bump workspace versions for aingle 0.9.0 --- Cargo.lock | 26 +++++++++++++------------- crates/aingle/Cargo.toml | 2 +- crates/aingle_ai/Cargo.toml | 2 +- crates/aingle_contracts/Cargo.toml | 2 +- crates/aingle_cortex/Cargo.toml | 10 +++++----- crates/aingle_graph/Cargo.toml | 2 +- crates/aingle_ingest/Cargo.toml | 4 ++-- crates/aingle_logic/Cargo.toml | 4 ++-- crates/aingle_minimal/Cargo.toml | 2 +- crates/aingle_raft/Cargo.toml | 4 ++-- crates/aingle_viz/Cargo.toml | 4 ++-- crates/aingle_wal/Cargo.toml | 2 +- crates/aingle_zk/Cargo.toml | 2 +- crates/ineru/Cargo.toml | 2 +- crates/kaneru/Cargo.toml | 2 +- 15 files changed, 35 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cac5e12a..42268eef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -99,7 +99,7 @@ dependencies = [ [[package]] name = "aingle_ai" -version = "0.7.1" +version = "0.7.2" dependencies = [ "blake2", "candle-core 0.9.2", @@ -121,7 +121,7 @@ dependencies = [ [[package]] name = "aingle_contracts" -version = "0.7.1" +version = "0.7.2" dependencies = [ "blake3", "dashmap 6.1.0", @@ -140,7 +140,7 @@ dependencies = [ [[package]] name = "aingle_cortex" -version = "0.8.0" +version = "0.9.0" dependencies = [ "aingle_graph", "aingle_ingest", @@ -201,7 +201,7 @@ dependencies = [ [[package]] name = "aingle_graph" -version = "0.8.0" +version = "0.9.0" dependencies = [ "bincode", "blake3", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "aingle_ingest" -version = "0.8.0" +version = "0.9.0" dependencies = [ "aingle_graph", "blake3", @@ -237,7 +237,7 @@ dependencies = [ [[package]] name = "aingle_logic" -version = "0.8.0" +version = "0.9.0" dependencies = [ "aingle_graph", "chrono", @@ -253,7 +253,7 @@ dependencies = [ [[package]] name = "aingle_minimal" -version = "0.7.1" +version = "0.7.2" dependencies = [ "async-io", "async-tungstenite", @@ -295,7 +295,7 @@ dependencies = [ [[package]] name = "aingle_raft" -version = "0.7.1" +version = "0.7.2" dependencies = [ "aingle_graph", "aingle_wal", @@ -316,7 +316,7 @@ dependencies = [ [[package]] name = "aingle_viz" -version = "0.7.1" +version = "0.7.2" dependencies = [ "aingle_graph", "aingle_minimal", @@ -338,7 +338,7 @@ dependencies = [ [[package]] name = "aingle_wal" -version = "0.7.1" +version = "0.7.2" dependencies = [ "bincode", "blake3", @@ -350,7 +350,7 @@ dependencies = [ [[package]] name = "aingle_zk" -version = "0.8.0" +version = "0.9.0" dependencies = [ "blake3", "bulletproofs", @@ -4264,7 +4264,7 @@ dependencies = [ [[package]] name = "ineru" -version = "0.7.5" +version = "0.7.6" dependencies = [ "bincode", "blake3", @@ -4524,7 +4524,7 @@ dependencies = [ [[package]] name = "kaneru" -version = "0.7.1" +version = "0.7.2" dependencies = [ "chrono", "criterion", diff --git a/crates/aingle/Cargo.toml b/crates/aingle/Cargo.toml index 31866633..e2dc2af3 100644 --- a/crates/aingle/Cargo.toml +++ b/crates/aingle/Cargo.toml @@ -33,7 +33,7 @@ aingle_sqlite = { version = "0.0.1", path = "../aingle_sqlite" } aingle_middleware_bytes = "=0.0.3" aingle_state = { version = "0.0.1", path = "../aingle_state" } aingle_types = { version = "0.0.1", path = "../aingle_types" } -aingle_cortex = { version = "0.8", path = "../aingle_cortex", default-features = false, features = ["rest"] } +aingle_cortex = { version = "0.9", path = "../aingle_cortex", default-features = false, features = ["rest"] } aingle_wasmer_host = "0.0.1" aingle_websocket = { version = "0.0.1", path = "../aingle_websocket" } wasmer = "=7.0.1" diff --git a/crates/aingle_ai/Cargo.toml b/crates/aingle_ai/Cargo.toml index 2dcf34df..b29f9c45 100644 --- a/crates/aingle_ai/Cargo.toml +++ b/crates/aingle_ai/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_ai" -version = "0.7.1" +version = "0.7.2" description = "AI integration layer for AIngle - Ineru, Nested Learning, Kaneru" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" diff --git a/crates/aingle_contracts/Cargo.toml b/crates/aingle_contracts/Cargo.toml index 71f74d5b..8b6aada2 100644 --- a/crates/aingle_contracts/Cargo.toml +++ b/crates/aingle_contracts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_contracts" -version = "0.7.1" +version = "0.7.2" description = "Smart Contracts DSL and WASM Runtime for AIngle" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" diff --git a/crates/aingle_cortex/Cargo.toml b/crates/aingle_cortex/Cargo.toml index 02f1a776..9ff1b99e 100644 --- a/crates/aingle_cortex/Cargo.toml +++ b/crates/aingle_cortex/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_cortex" -version = "0.8.0" +version = "0.9.0" description = "Córtex API - REST/GraphQL/SPARQL interface for AIngle semantic graphs" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" @@ -35,11 +35,11 @@ path = "src/main.rs" [dependencies] # Core AIngle crates -aingle_graph = { version = "0.8", path = "../aingle_graph", features = ["sled-backend"] } -aingle_logic = { version = "0.8", path = "../aingle_logic" } -aingle_zk = { version = "0.8", path = "../aingle_zk" } +aingle_graph = { version = "0.9", path = "../aingle_graph", features = ["sled-backend"] } +aingle_logic = { version = "0.9", path = "../aingle_logic" } +aingle_zk = { version = "0.9", path = "../aingle_zk" } ineru = { version = "0.7", path = "../ineru" } -aingle_ingest = { version = "0.8", path = "../aingle_ingest" } +aingle_ingest = { version = "0.9", path = "../aingle_ingest" } ignore = "0.4" # Web framework diff --git a/crates/aingle_graph/Cargo.toml b/crates/aingle_graph/Cargo.toml index 83b48b23..fa437304 100644 --- a/crates/aingle_graph/Cargo.toml +++ b/crates/aingle_graph/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_graph" -version = "0.8.0" +version = "0.9.0" description = "Native GraphDB for AIngle - Semantic triple store with SPO indexes" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" diff --git a/crates/aingle_ingest/Cargo.toml b/crates/aingle_ingest/Cargo.toml index 2617c120..20453e06 100644 --- a/crates/aingle_ingest/Cargo.toml +++ b/crates/aingle_ingest/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "aingle_ingest" -version = "0.8.0" +version = "0.9.0" description = "Structural extraction of triples and text chunks from markdown/code for AIngle" license = "Apache-2.0 OR LicenseRef-Commercial" edition = "2021" rust-version.workspace = true [dependencies] -aingle_graph = { version = "0.8", path = "../aingle_graph", features = ["dag"] } +aingle_graph = { version = "0.9", path = "../aingle_graph", features = ["dag"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" regex = "1.12" diff --git a/crates/aingle_logic/Cargo.toml b/crates/aingle_logic/Cargo.toml index ce81182b..c6dbb501 100644 --- a/crates/aingle_logic/Cargo.toml +++ b/crates/aingle_logic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_logic" -version = "0.8.0" +version = "0.9.0" description = "Proof-of-Logic validation engine for AIngle semantic graphs" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" @@ -20,7 +20,7 @@ owl = [] [dependencies] # Graph database -aingle_graph = { version = "0.8", path = "../aingle_graph" } +aingle_graph = { version = "0.9", path = "../aingle_graph" } # Serialization serde = { version = "1.0", features = ["derive"] } diff --git a/crates/aingle_minimal/Cargo.toml b/crates/aingle_minimal/Cargo.toml index 41f5d6f4..b09e98bc 100644 --- a/crates/aingle_minimal/Cargo.toml +++ b/crates/aingle_minimal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_minimal" -version = "0.7.1" +version = "0.7.2" description = "Ultra-light AIngle node for IoT devices (<1MB RAM)" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" diff --git a/crates/aingle_raft/Cargo.toml b/crates/aingle_raft/Cargo.toml index afd50472..4414e474 100644 --- a/crates/aingle_raft/Cargo.toml +++ b/crates/aingle_raft/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_raft" -version = "0.7.1" +version = "0.7.2" description = "Raft consensus for AIngle clustering" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" @@ -27,7 +27,7 @@ tracing = "0.1" chrono = { version = "0.4", features = ["serde"] } futures-util = "0.3" anyerror = "0.1" -aingle_graph = { version = "0.8", path = "../aingle_graph", features = ["sled-backend"] } +aingle_graph = { version = "0.9", path = "../aingle_graph", features = ["sled-backend"] } ineru = { version = "0.7", path = "../ineru" } [dev-dependencies] diff --git a/crates/aingle_viz/Cargo.toml b/crates/aingle_viz/Cargo.toml index 6bf1c6cf..a3a6bbf3 100644 --- a/crates/aingle_viz/Cargo.toml +++ b/crates/aingle_viz/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_viz" -version = "0.7.1" +version = "0.7.2" description = "DAG Visualization for AIngle - Web-based graph explorer" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" @@ -29,7 +29,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" # Graph data -aingle_graph = { version = "0.8", path = "../aingle_graph" } +aingle_graph = { version = "0.9", path = "../aingle_graph" } aingle_minimal = { version = "0.7", path = "../aingle_minimal", default-features = false, features = ["sqlite"] } # Utilities diff --git a/crates/aingle_wal/Cargo.toml b/crates/aingle_wal/Cargo.toml index 6ad89fa8..862cb538 100644 --- a/crates/aingle_wal/Cargo.toml +++ b/crates/aingle_wal/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_wal" -version = "0.7.1" +version = "0.7.2" description = "Write-Ahead Log for AIngle clustering and replication" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" diff --git a/crates/aingle_zk/Cargo.toml b/crates/aingle_zk/Cargo.toml index 918db1f2..2fc97970 100644 --- a/crates/aingle_zk/Cargo.toml +++ b/crates/aingle_zk/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "aingle_zk" -version = "0.8.0" +version = "0.9.0" description = "Zero-Knowledge Proofs for AIngle - privacy-preserving cryptographic primitives" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" diff --git a/crates/ineru/Cargo.toml b/crates/ineru/Cargo.toml index cbfbffd4..04bb57b4 100644 --- a/crates/ineru/Cargo.toml +++ b/crates/ineru/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "ineru" -version = "0.7.5" +version = "0.7.6" description = "Ineru: Neural-inspired memory system for AIngle AI agents" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle" diff --git a/crates/kaneru/Cargo.toml b/crates/kaneru/Cargo.toml index 25820958..6107f079 100644 --- a/crates/kaneru/Cargo.toml +++ b/crates/kaneru/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kaneru" -version = "0.7.1" +version = "0.7.2" description = "Kaneru: Unified Multi-Agent Execution System for AIngle AI agents" license = "Apache-2.0 OR LicenseRef-Commercial" repository = "https://github.com/ApiliumCode/aingle"