From daf3fe0ee14c484714fe78fefe5a25a2cba6b589 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 20 Sep 2026 16:10:25 +0000 Subject: [PATCH] fix(ci): unbreak Signal Chain CI (fmt, clippy, windows shell) Three independent root causes for the red main CI in Signal Chain CI: - `cargo fmt` job: openshell-signal-chain wasn't rustfmt-clean. Ran `cargo fmt -p openshell-signal-chain` (formatting only, no logic change). - test-ubuntu/test-macos: `cargo clippy` failed with "'cargo-clippy' is not installed for the toolchain" because the "Configure Rust" step never installs the clippy component. Added `rustup component add clippy`. - test-windows: the "Configure Rust" step uses bash-style `2>/dev/null` redirection but runs under the runner's default shell on Windows (pwsh), which tried to resolve /dev/null as a literal path and failed immediately. Added `shell: bash` so the step runs consistently across all three OSes. Verified locally: `cargo fmt -p openshell-signal-chain -- --check`, `cargo test -p openshell-signal-chain`, and `cargo clippy -p openshell-signal-chain -- -D warnings` all pass. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01JV2C8fnD86DNwXoyHPd7X9 --- .github/workflows/signal-chain-ci.yml | 2 + .../benches/bench_dial.rs | 36 ++--- .../benches/bench_room.rs | 2 +- .../openshell-signal-chain/benches/cascade.rs | 2 +- .../benches/signal_chain_benches.rs | 153 +++++++++++++----- .../examples/basic_dial.rs | 83 +++++++--- .../examples/fleet_operations.rs | 117 ++++++++------ .../examples/signal_chain_room.rs | 32 +++- .../examples/spline_curvature.rs | 36 +++-- .../examples/vm_integration.rs | 2 +- .../openshell-signal-chain/src/constraint.rs | 5 +- crates/openshell-signal-chain/src/error.rs | 1 - .../openshell-signal-chain/src/inference.rs | 2 +- crates/openshell-signal-chain/src/lib.rs | 21 +-- crates/openshell-signal-chain/src/room.rs | 2 +- .../src/signal_chain.rs | 41 +++-- crates/openshell-signal-chain/src/snap.rs | 2 +- .../src/spline_connector.rs | 46 ++++-- .../tests/test_signal_chain.rs | 60 +++++-- 19 files changed, 433 insertions(+), 212 deletions(-) diff --git a/.github/workflows/signal-chain-ci.yml b/.github/workflows/signal-chain-ci.yml index 22a61f427b..1246bb56f7 100644 --- a/.github/workflows/signal-chain-ci.yml +++ b/.github/workflows/signal-chain-ci.yml @@ -64,8 +64,10 @@ jobs: fetch-depth: 0 - name: Configure Rust + shell: bash run: | rustup target add "${{ matrix.target }}" 2>/dev/null || true + rustup component add clippy 2>/dev/null || true rustup show - name: cargo check diff --git a/crates/openshell-signal-chain/benches/bench_dial.rs b/crates/openshell-signal-chain/benches/bench_dial.rs index f6b817b173..3f387332f3 100644 --- a/crates/openshell-signal-chain/benches/bench_dial.rs +++ b/crates/openshell-signal-chain/benches/bench_dial.rs @@ -8,7 +8,7 @@ //! Note: Run `cargo bench` from the crate directory, or use: //! cargo bench -p openshell-signal-chain -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use openshell_signal_chain::{Dial, Room}; /// Pre-populate a room with snaps and inferences for benchmarking @@ -21,7 +21,7 @@ fn setup_room(item_count: usize, inference_ratio: f64) -> Room { for i in 0..snap_count { room.add_snap( serde_json::json!({"id": i, "type": "snap", "data": format!("snap-{}", i)}), - 1.0 + 1.0, ); } @@ -30,7 +30,7 @@ fn setup_room(item_count: usize, inference_ratio: f64) -> Room { let confidence = (i as f64) / (inference_count as f64); room.add_inference( serde_json::json!({"id": i, "type": "inference", "data": format!("inf-{}", i)}), - confidence.max(0.1) + confidence.max(0.1), ); } @@ -42,16 +42,11 @@ fn bench_dial_query(c: &mut Criterion) { // Vary dial position for pos in [0.0, 0.25, 0.5, 0.75, 1.0].iter() { - group.bench_with_input( - BenchmarkId::from_parameter(pos), pos, - |b, &pos| { - let room = setup_room(100, 0.5); - let dial = Dial::new(pos); - b.iter(|| { - black_box(room.query(black_box(dial))) - }); - } - ); + group.bench_with_input(BenchmarkId::from_parameter(pos), pos, |b, &pos| { + let room = setup_room(100, 0.5); + let dial = Dial::new(pos); + b.iter(|| black_box(room.query(black_box(dial)))); + }); } group.finish(); @@ -80,16 +75,11 @@ fn bench_room_scales(c: &mut Criterion) { // Vary room size for size in [10, 50, 100, 500, 1000].iter() { - group.bench_with_input( - BenchmarkId::from_parameter(size), size, - |b, &size| { - let room = setup_room(size, 0.5); - let dial = Dial::new(0.5); - b.iter(|| { - black_box(room.query(black_box(dial))) - }); - } - ); + group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let room = setup_room(size, 0.5); + let dial = Dial::new(0.5); + b.iter(|| black_box(room.query(black_box(dial)))); + }); } group.finish(); diff --git a/crates/openshell-signal-chain/benches/bench_room.rs b/crates/openshell-signal-chain/benches/bench_room.rs index 76822390e6..2ec84abb95 100644 --- a/crates/openshell-signal-chain/benches/bench_room.rs +++ b/crates/openshell-signal-chain/benches/bench_room.rs @@ -8,7 +8,7 @@ //! Note: Run `cargo bench` from the crate directory, or use: //! cargo bench -p openshell-signal-chain -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use openshell_signal_chain::{Dial, Room, SignalChain}; /// Pre-populate a room with snaps and inferences for benchmarking diff --git a/crates/openshell-signal-chain/benches/cascade.rs b/crates/openshell-signal-chain/benches/cascade.rs index c6346326a1..c4dc53cc86 100644 --- a/crates/openshell-signal-chain/benches/cascade.rs +++ b/crates/openshell-signal-chain/benches/cascade.rs @@ -5,7 +5,7 @@ //! //! Run with: cargo bench -p openshell-signal-chain --bench cascade -use criterion::{BenchmarkId, Criterion, black_box, criterion_group, criterion_main}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use openshell_signal_chain::{Room, SignalChain}; /// Build a room tree of given depth and branching factor, with n inferences at the root. diff --git a/crates/openshell-signal-chain/benches/signal_chain_benches.rs b/crates/openshell-signal-chain/benches/signal_chain_benches.rs index ddb8528245..79af261640 100644 --- a/crates/openshell-signal-chain/benches/signal_chain_benches.rs +++ b/crates/openshell-signal-chain/benches/signal_chain_benches.rs @@ -4,16 +4,32 @@ //! Criterion benchmarks for openshell-signal-chain //! Run with: cargo bench -p openshell-signal-chain -use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId}; -use openshell_signal_chain::{Dial, Room, SignalChain, SplineConstraint, HolonomyRoom, BettiResult}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; +use openshell_signal_chain::{ + BettiResult, Dial, HolonomyRoom, Room, SignalChain, SplineConstraint, +}; fn bench_dial(c: &mut Criterion) { let mut g = c.benchmark_group("Dial"); - g.bench_function("new_hard", |b| b.iter(|| { let d = Dial::hard(); black_box(d.position); })); - g.bench_function("new_soft", |b| b.iter(|| { let d = Dial::soft(); black_box(d.position); })); + g.bench_function("new_hard", |b| { + b.iter(|| { + let d = Dial::hard(); + black_box(d.position); + }) + }); + g.bench_function("new_soft", |b| { + b.iter(|| { + let d = Dial::soft(); + black_box(d.position); + }) + }); for pos in [0.0, 0.25, 0.5, 0.75, 1.0] { g.bench_with_input(BenchmarkId::from_parameter(pos), &pos, |b, &p| { - b.iter(|| { let d = Dial::new(p); black_box(d.snap_weight()); black_box(d.inference_threshold()); }); + b.iter(|| { + let d = Dial::new(p); + black_box(d.snap_weight()); + black_box(d.inference_threshold()); + }); }); } g.finish(); @@ -24,29 +40,46 @@ fn bench_spline(c: &mut Criterion) { let wave = SplineConstraint::new(0, 127, "wave_height_dm", 1.5, 1.5, 3.0); for val in [10_i32, 35, 60, 90, 120] { g.bench_with_input(BenchmarkId::from_parameter(val), &val, |b, &v| { - b.iter(|| { let d = wave.curvature_distance(v); black_box(d); }); + b.iter(|| { + let d = wave.curvature_distance(v); + black_box(d); + }); }); } - g.bench_function("maritime_preset", |b| b.iter(|| { - use openshell_signal_chain::maritime_spline; - black_box(maritime_spline()) - })); + g.bench_function("maritime_preset", |b| { + b.iter(|| { + use openshell_signal_chain::maritime_spline; + black_box(maritime_spline()) + }) + }); g.finish(); } fn bench_room(c: &mut Criterion) { let mut g = c.benchmark_group("Room"); - g.bench_function("new", |b| b.iter(|| { let r = Room::new("t"); black_box(r); })); + g.bench_function("new", |b| { + b.iter(|| { + let r = Room::new("t"); + black_box(r); + }) + }); let room = { let mut r = Room::new("t"); - for i in 0..100 { r.add_snap(serde_json::json!({"i":i}), 1.0); } - for i in 0..50 { r.add_inference(serde_json::json!({"i":i}), 0.7); } + for i in 0..100 { + r.add_snap(serde_json::json!({"i":i}), 1.0); + } + for i in 0..50 { + r.add_inference(serde_json::json!({"i":i}), 0.7); + } r }; for d in [0.0, 0.3, 0.6, 1.0] { g.bench_with_input(BenchmarkId::from_parameter(d), &d, |b, &p| { - b.iter(|| { let res = room.query(Dial::new(p)); black_box(res); }); + b.iter(|| { + let res = room.query(Dial::new(p)); + black_box(res); + }); }); } g.finish(); @@ -54,57 +87,93 @@ fn bench_room(c: &mut Criterion) { fn bench_holonomy(c: &mut Criterion) { let mut g = c.benchmark_group("HolonomyRoom"); - let cases = vec![(3,3),(5,7),(10,17),(20,37),(4,10),(10,30)]; + let cases = vec![(3, 3), (5, 7), (10, 17), (20, 37), (4, 10), (10, 30)]; for &(v, e) in &cases { - g.bench_with_input(BenchmarkId::new("betti", format!("V{}E{}",v,e)), &(v, e), |b, &(v, e)| { - b.iter(|| { - let mut r = HolonomyRoom::new("b", Dial::hard()); - for _ in 0..v { r.add_snap(); } - for _ in 0..e { r.add_edge(); } - black_box(r.betti()); - }); - }); + g.bench_with_input( + BenchmarkId::new("betti", format!("V{}E{}", v, e)), + &(v, e), + |b, &(v, e)| { + b.iter(|| { + let mut r = HolonomyRoom::new("b", Dial::hard()); + for _ in 0..v { + r.add_snap(); + } + for _ in 0..e { + r.add_edge(); + } + black_box(r.betti()); + }); + }, + ); } - g.bench_function("betti_pure", |b| b.iter(|| { - black_box(BettiResult::compute(17, 10, 1)); - })); + g.bench_function("betti_pure", |b| { + b.iter(|| { + black_box(BettiResult::compute(17, 10, 1)); + }) + }); g.finish(); } fn bench_chain(c: &mut Criterion) { let mut g = c.benchmark_group("SignalChain"); - g.bench_function("new", |b| b.iter(|| { black_box(SignalChain::new("f")); })); + g.bench_function("new", |b| { + b.iter(|| { + black_box(SignalChain::new("f")); + }) + }); let chain = { let mut ch = SignalChain::new("f"); - for n in ["nav","sonar","weather","analysis"] { + for n in ["nav", "sonar", "weather", "analysis"] { ch.room(n); } ch }; for d in [0.0, 0.5, 1.0] { - g.bench_with_input(BenchmarkId::from_parameter(d), &d, |b,&p| { - b.iter(|| { black_box(chain.query_all(Dial::new(p))); }); + g.bench_with_input(BenchmarkId::from_parameter(d), &d, |b, &p| { + b.iter(|| { + black_box(chain.query_all(Dial::new(p))); + }); }); } - g.bench_function("traverse", |b| b.iter(|| { black_box(chain.traverse(&["nav","sonar","weather","analysis"])); })); + g.bench_function("traverse", |b| { + b.iter(|| { + black_box(chain.traverse(&["nav", "sonar", "weather", "analysis"])); + }) + }); g.finish(); } fn bench_tput(c: &mut Criterion) { let mut g = c.benchmark_group("Throughput"); - g.bench_function("add_snap_10k", |b| b.iter(|| { - let mut r = Room::new("t"); - for _ in 0..10_000 { r.add_snap(serde_json::json!({"x":1}), 1.0); } - black_box(r) - })); - g.bench_function("add_inference_10k", |b| b.iter(|| { - let mut r = Room::new("t"); - for _ in 0..10_000 { r.add_inference(serde_json::json!({"x":1}), 0.75); } - black_box(r) - })); + g.bench_function("add_snap_10k", |b| { + b.iter(|| { + let mut r = Room::new("t"); + for _ in 0..10_000 { + r.add_snap(serde_json::json!({"x":1}), 1.0); + } + black_box(r) + }) + }); + g.bench_function("add_inference_10k", |b| { + b.iter(|| { + let mut r = Room::new("t"); + for _ in 0..10_000 { + r.add_inference(serde_json::json!({"x":1}), 0.75); + } + black_box(r) + }) + }); g.finish(); } -criterion_group!(benches, bench_dial, bench_spline, bench_room, bench_holonomy, bench_chain, bench_tput); +criterion_group!( + benches, + bench_dial, + bench_spline, + bench_room, + bench_holonomy, + bench_chain, + bench_tput +); criterion_main!(benches); diff --git a/crates/openshell-signal-chain/examples/basic_dial.rs b/crates/openshell-signal-chain/examples/basic_dial.rs index fac4b54862..f8c1cb7f49 100644 --- a/crates/openshell-signal-chain/examples/basic_dial.rs +++ b/crates/openshell-signal-chain/examples/basic_dial.rs @@ -12,28 +12,40 @@ fn main() { // 1. Create a room and add snaps (hard facts) let mut room = Room::new("sonar-readings"); - room.add_snap(serde_json::json!({ - "type": "depth", - "value": 42.5, - "unit": "meters" - }), 1.0); - room.add_snap(serde_json::json!({ - "type": "temperature", - "value": 12.3, - "unit": "celsius" - }), 1.0); + room.add_snap( + serde_json::json!({ + "type": "depth", + "value": 42.5, + "unit": "meters" + }), + 1.0, + ); + room.add_snap( + serde_json::json!({ + "type": "temperature", + "value": 12.3, + "unit": "celsius" + }), + 1.0, + ); // 2. Add inferences (soft extrapolations) - room.add_inference(serde_json::json!({ - "type": "prediction", - "hypothesis": "depth will decrease", - "delta": -2.1 - }), 0.75); - room.add_inference(serde_json::json!({ - "type": "speculation", - "hypothesis": "fish school detected nearby", - "confidence_signal": 0.3 - }), 0.3); + room.add_inference( + serde_json::json!({ + "type": "prediction", + "hypothesis": "depth will decrease", + "delta": -2.1 + }), + 0.75, + ); + room.add_inference( + serde_json::json!({ + "type": "speculation", + "hypothesis": "fish school detected nearby", + "confidence_signal": 0.3 + }), + 0.3, + ); // 3. Query at different dial levels println!("--- Dial: HARD (0.0) ---"); @@ -64,11 +76,26 @@ fn main() { // 4. Use preset dials println!("\n--- Using Preset Dials ---"); - println!(" DIAL_FORMAL (formal reasoning): position = {:.1}", openshell_signal_chain::DIAL_FORMAL.position); - println!(" DIAL_BATHY (bathymetric data): position = {:.1}", openshell_signal_chain::DIAL_BATHY.position); - println!(" DIAL_COMMIT (git history): position = {:.1}", openshell_signal_chain::DIAL_COMMIT.position); - println!(" DIAL_REVIEW (balanced): position = {:.1}", openshell_signal_chain::DIAL_REVIEW.position); - println!(" DIAL_CREATIVE (generative): position = {:.1}", openshell_signal_chain::DIAL_CREATIVE.position); + println!( + " DIAL_FORMAL (formal reasoning): position = {:.1}", + openshell_signal_chain::DIAL_FORMAL.position + ); + println!( + " DIAL_BATHY (bathymetric data): position = {:.1}", + openshell_signal_chain::DIAL_BATHY.position + ); + println!( + " DIAL_COMMIT (git history): position = {:.1}", + openshell_signal_chain::DIAL_COMMIT.position + ); + println!( + " DIAL_REVIEW (balanced): position = {:.1}", + openshell_signal_chain::DIAL_REVIEW.position + ); + println!( + " DIAL_CREATIVE (generative): position = {:.1}", + openshell_signal_chain::DIAL_CREATIVE.position + ); // 5. Query snaps and inferences separately println!("\n--- Separate Queries ---"); @@ -76,7 +103,11 @@ fn main() { println!(" Snaps only ({}): {:?}", snaps.len(), snaps); let inferences = room.query_inferences(Dial::new(0.5)); - println!(" Inferences at 0.5 ({}): {:?}", inferences.len(), inferences); + println!( + " Inferences at 0.5 ({}): {:?}", + inferences.len(), + inferences + ); // 6. Demonstrate threshold checking println!("\n--- Inference Threshold Demo ---"); diff --git a/crates/openshell-signal-chain/examples/fleet_operations.rs b/crates/openshell-signal-chain/examples/fleet_operations.rs index 9ce9547d58..3d66c7944b 100644 --- a/crates/openshell-signal-chain/examples/fleet_operations.rs +++ b/crates/openshell-signal-chain/examples/fleet_operations.rs @@ -23,63 +23,86 @@ fn main() { // Sonar array — hard sensor data let sonar = chain.room("sonar-array"); - sonar.add_snap(serde_json::json!({ - "contact": "solid", - "bearing": 127.4, - "range_m": 200, - "depth_m": 87.2 - }), 1.0); - sonar.add_snap(serde_json::json!({ - "contact": "weak", - "bearing": 130.1, - "range_m": 350, - "depth_m": 92.0 - }), 0.7); - sonar.add_inference(serde_json::json!({ - "hypothesis": "large metal object at bearing 127-130", - "confidence_basis": "two contacts in proximity" - }), 0.75); + sonar.add_snap( + serde_json::json!({ + "contact": "solid", + "bearing": 127.4, + "range_m": 200, + "depth_m": 87.2 + }), + 1.0, + ); + sonar.add_snap( + serde_json::json!({ + "contact": "weak", + "bearing": 130.1, + "range_m": 350, + "depth_m": 92.0 + }), + 0.7, + ); + sonar.add_inference( + serde_json::json!({ + "hypothesis": "large metal object at bearing 127-130", + "confidence_basis": "two contacts in proximity" + }), + 0.75, + ); // Navigation — position and heading facts let nav = chain.room("navigation"); - nav.add_snap(serde_json::json!({ - "lat": 45.321, - "lon": -122.845, - "heading_deg": 125.0, - "speed_knots": 4.2 - }), 1.0); - nav.add_inference(serde_json::json!({ - "hypothesis": "current drift: 0.3 kts east", - "basis": "GPS vs dead-reckoning delta" - }), 0.6); + nav.add_snap( + serde_json::json!({ + "lat": 45.321, + "lon": -122.845, + "heading_deg": 125.0, + "speed_knots": 4.2 + }), + 1.0, + ); + nav.add_inference( + serde_json::json!({ + "hypothesis": "current drift: 0.3 kts east", + "basis": "GPS vs dead-reckoning delta" + }), + 0.6, + ); // Analysis — human/ML interpretation (softer) chain.room_with_dial("analysis", Dial::new(0.6)); let analysis = chain.room("analysis"); - analysis.add_inference(serde_json::json!({ - "classification": "possible shipwreck", - "era_estimate": "early 1900s", - "certainty": "moderate" - }), 0.65); - analysis.add_inference(serde_json::json!({ - "classification": "natural rock formation", - "certainty": "low" - }), 0.3); + analysis.add_inference( + serde_json::json!({ + "classification": "possible shipwreck", + "era_estimate": "early 1900s", + "certainty": "moderate" + }), + 0.65, + ); + analysis.add_inference( + serde_json::json!({ + "classification": "natural rock formation", + "certainty": "low" + }), + 0.3, + ); // Formal proof room — only hard facts chain.room_with_dial("formal-proofs", Dial::hard()); let proofs = chain.room("formal-proofs"); - proofs.add_snap(serde_json::json!({ - "theorem": "drift_detected", - "proof": "sonar_variance > threshold(3σ)" - }), 1.0); + proofs.add_snap( + serde_json::json!({ + "theorem": "drift_detected", + "proof": "sonar_variance > threshold(3σ)" + }), + 1.0, + ); // 3. Set up hierarchy: analysis room has sub-rooms let analysis_room = chain.room("analysis"); - analysis_room.children.insert( - "classification".to_string(), - Room::new("classification"), - ); + analysis_room + .children + .insert("classification".to_string(), Room::new("classification")); analysis_room.children.insert( "historical-context".to_string(), Room::new("historical-context"), @@ -103,10 +126,10 @@ fn main() { ); for (child_name, child) in &room.children { println!( - " └─ {} (snaps={}, inferences={})", - child_name, - child.snaps.len(), - child.inferences.len(), + " └─ {} (snaps={}, inferences={})", + child_name, + child.snaps.len(), + child.inferences.len(), ); } } diff --git a/crates/openshell-signal-chain/examples/signal_chain_room.rs b/crates/openshell-signal-chain/examples/signal_chain_room.rs index 55f1fa4cfb..721ade1cb8 100644 --- a/crates/openshell-signal-chain/examples/signal_chain_room.rs +++ b/crates/openshell-signal-chain/examples/signal_chain_room.rs @@ -28,7 +28,10 @@ fn main() { let mut bathy_room = Room::with_dial("bathy-data", DIAL_BATHY); bathy_room.add_absolute(serde_json::json!({"depth": 250.5, "sensor": "multibeam"})); bathy_room.add_inference(serde_json::json!({"seabed_type": "sand"}), 0.7); - println!(" Room dial position: {:.1}", bathy_room.dial_position.position); + println!( + " Room dial position: {:.1}", + bathy_room.dial_position.position + ); // 3. SignalChain with multiple rooms println!("\n--- 3. SignalChain ---"); @@ -65,8 +68,12 @@ fn main() { println!("\n--- 4. Room Traversal ---"); let rooms = chain.traverse(&["navigation", "catch-log", "weather"]); for room in rooms { - println!(" Traversed: {} (snaps: {}, inferences: {})", - room.name, room.snaps.len(), room.inferences.len()); + println!( + " Traversed: {} (snaps: {}, inferences: {})", + room.name, + room.snaps.len(), + room.inferences.len() + ); } // 5. Cascade demonstration @@ -103,14 +110,23 @@ fn main() { // 6. Room metadata println!("\n--- 6. Room Metadata ---"); let mut meta_room = Room::new("annotated"); - meta_room.metadata.insert("vessel".to_string(), serde_json::json!("MSC-Seeker")); - meta_room.metadata.insert("captain".to_string(), serde_json::json!("Ng")); - meta_room.metadata.insert("date".to_string(), serde_json::json!("2026-05-20")); + meta_room + .metadata + .insert("vessel".to_string(), serde_json::json!("MSC-Seeker")); + meta_room + .metadata + .insert("captain".to_string(), serde_json::json!("Ng")); + meta_room + .metadata + .insert("date".to_string(), serde_json::json!("2026-05-20")); meta_room.add_snap(serde_json::json!({"catch": "tuna"}), 1.0); println!(" Metadata: {:?}", meta_room.metadata); - println!(" Room complete: {} with {} snaps", - meta_room.name, meta_room.snaps.len()); + println!( + " Room complete: {} with {} snaps", + meta_room.name, + meta_room.snaps.len() + ); println!("\n=== Done ==="); } diff --git a/crates/openshell-signal-chain/examples/spline_curvature.rs b/crates/openshell-signal-chain/examples/spline_curvature.rs index 1923337738..1a556086a0 100644 --- a/crates/openshell-signal-chain/examples/spline_curvature.rs +++ b/crates/openshell-signal-chain/examples/spline_curvature.rs @@ -10,7 +10,7 @@ //! //! Run with: cargo run --example spline_curvature -p openshell-signal-chain -use openshell_signal_chain::{Dial, SplineConstraint, evaluate_spline, maritime_spline}; +use openshell_signal_chain::{evaluate_spline, maritime_spline, Dial, SplineConstraint}; fn main() { println!("=== Spline Curvature: Fare Curves of Truth ===\n"); @@ -35,15 +35,27 @@ fn main() { match evaluate_spline(&constraints, normal_values, dial) { Ok(r) => println!( " dial={:.1} ({}): PASS — curvature={:.3}, hard_pass={}", - dial_pos, - if dial_pos < 0.25 { "hard" } else if dial_pos < 0.75 { "balanced" } else { "soft" }, + dial_pos, + if dial_pos < 0.25 { + "hard" + } else if dial_pos < 0.75 { + "balanced" + } else { + "soft" + }, r.total_curvature, r.is_hard_pass ), Err(v) => println!( " dial={:.1} ({}): FAIL — {} violation(s)", dial_pos, - if dial_pos < 0.25 { "hard" } else if dial_pos < 0.75 { "balanced" } else { "soft" }, + if dial_pos < 0.25 { + "hard" + } else if dial_pos < 0.75 { + "balanced" + } else { + "soft" + }, v.len() ), } @@ -79,12 +91,12 @@ fn main() { // Custom spline: depth sounding let depth_spline = SplineConstraint::new( - 0, // 0 fathoms (surface) - 120, // 120 fathoms (safe working depth) + 0, // 0 fathoms (surface) + 120, // 120 fathoms (safe working depth) "depth_fathoms", - 1.3, // cost increases sharply near surface (wave action) - 1.0, // cost near bottom is moderate - 0.4, // neutral zone is deeper (working depth) + 1.3, // cost increases sharply near surface (wave action) + 1.0, // cost near bottom is moderate + 0.4, // neutral zone is deeper (working depth) ); println!("Depth spline (neutral zone at 40% of range):"); @@ -98,7 +110,11 @@ fn main() { value, curvature, if hard_result.is_ok() { "PASS" } else { "FAIL" }, - if soft_result.is_ok() { "ok" } else { "advisory" } + if soft_result.is_ok() { + "ok" + } else { + "advisory" + } ); if let Err(v) = hard_result { print!(" (severity={:?})", v.severity); diff --git a/crates/openshell-signal-chain/examples/vm_integration.rs b/crates/openshell-signal-chain/examples/vm_integration.rs index b6f5204a78..611db1632c 100644 --- a/crates/openshell-signal-chain/examples/vm_integration.rs +++ b/crates/openshell-signal-chain/examples/vm_integration.rs @@ -5,7 +5,7 @@ //! //! Run with: cargo run --example vm_integration -p openshell-signal-chain -use openshell_signal_chain::{Dial, HolonomyRoom, SignalChain, maritime_spline}; +use openshell_signal_chain::{maritime_spline, Dial, HolonomyRoom, SignalChain}; fn main() { println!("=== Signal Chain × Spline × Holonomy Integration ===\n"); diff --git a/crates/openshell-signal-chain/src/constraint.rs b/crates/openshell-signal-chain/src/constraint.rs index 566daa3d77..45554a4bd5 100644 --- a/crates/openshell-signal-chain/src/constraint.rs +++ b/crates/openshell-signal-chain/src/constraint.rs @@ -243,7 +243,10 @@ impl SplineResult { self.violations.is_empty() } else if dial.position < 0.75 { // Balanced mode: warnings only - !self.violations.iter().any(|v| v.severity == ViolationSeverity::Critical) + !self + .violations + .iter() + .any(|v| v.severity == ViolationSeverity::Critical) } else { // Soft mode: anything goes true diff --git a/crates/openshell-signal-chain/src/error.rs b/crates/openshell-signal-chain/src/error.rs index eaac8b2556..e71a5acc12 100644 --- a/crates/openshell-signal-chain/src/error.rs +++ b/crates/openshell-signal-chain/src/error.rs @@ -52,5 +52,4 @@ pub enum SignalChainError { /// Contains a description of the cycle path. #[error("Cascade cycle detected: {0}")] CascadeCycle(String), - } diff --git a/crates/openshell-signal-chain/src/inference.rs b/crates/openshell-signal-chain/src/inference.rs index a7498f8efc..f4ef6f771c 100644 --- a/crates/openshell-signal-chain/src/inference.rs +++ b/crates/openshell-signal-chain/src/inference.rs @@ -23,8 +23,8 @@ //! assert!(inf.confidence >= 0.8); //! ``` -use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; /// A soft extrapolation/hypothesis in a room. /// diff --git a/crates/openshell-signal-chain/src/lib.rs b/crates/openshell-signal-chain/src/lib.rs index c190ca7cda..5db5358fca 100644 --- a/crates/openshell-signal-chain/src/lib.rs +++ b/crates/openshell-signal-chain/src/lib.rs @@ -72,30 +72,25 @@ pub use signal_chain::SignalChain; pub use snap::Snap; // Preset dials for common use cases -pub use dial::DIAL_FORMAL; +pub use dial::DIAL_ANALYSIS; pub use dial::DIAL_BATHY; pub use dial::DIAL_COMMIT; -pub use dial::DIAL_ANALYSIS; -pub use dial::DIAL_REVIEW; -pub use dial::DIAL_EXTRAPOLATE; pub use dial::DIAL_CREATIVE; pub use dial::DIAL_EXPLORATORY; +pub use dial::DIAL_EXTRAPOLATE; +pub use dial::DIAL_FORMAL; +pub use dial::DIAL_REVIEW; pub mod constraint; pub use constraint::{ - SplineConstraint, SplineResult, ConstraintViolation, ViolationSeverity, - evaluate_spline, maritime_spline, + evaluate_spline, maritime_spline, ConstraintViolation, SplineConstraint, SplineResult, + ViolationSeverity, }; pub mod spline_connector; -pub use spline_connector::{ - SplineRoom, SplineChain, SplineEvaluation, -}; +pub use spline_connector::{SplineChain, SplineEvaluation, SplineRoom}; pub mod holonomy_bridge; -pub use holonomy_bridge::{ - HolonomyRoom, HolonomyChain, HolonomyStatus, BettiResult, -}; +pub use holonomy_bridge::{BettiResult, HolonomyChain, HolonomyRoom, HolonomyStatus}; pub mod flux_vm_bridge; pub use flux_vm_bridge::FluxVmBridge; - diff --git a/crates/openshell-signal-chain/src/room.rs b/crates/openshell-signal-chain/src/room.rs index fa06955663..486e78a418 100644 --- a/crates/openshell-signal-chain/src/room.rs +++ b/crates/openshell-signal-chain/src/room.rs @@ -491,7 +491,7 @@ mod tests { let child = parent.children.get("child").unwrap(); assert_eq!(child.snaps.len(), 2); // top 2 by confidence - // Highest confidence should be first cascaded + // Highest confidence should be first cascaded assert_eq!(child.snaps[0].confidence, 0.95 * 0.8); assert_eq!(child.snaps[1].confidence, 0.7 * 0.8); } diff --git a/crates/openshell-signal-chain/src/signal_chain.rs b/crates/openshell-signal-chain/src/signal_chain.rs index 3517bc9a9f..179df5d06c 100644 --- a/crates/openshell-signal-chain/src/signal_chain.rs +++ b/crates/openshell-signal-chain/src/signal_chain.rs @@ -25,8 +25,8 @@ //! assert_eq!(all_results.len(), 2); //! ``` -use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use super::{Dial, Inference, Room, SignalChainError}; @@ -145,7 +145,8 @@ impl SignalChain { /// ``` pub fn room(&mut self, name: &str) -> &mut Room { if !self.rooms.contains_key(name) { - self.rooms.insert(name.to_string(), Room::with_dial(name, self.global_dial)); + self.rooms + .insert(name.to_string(), Room::with_dial(name, self.global_dial)); } self.rooms.get_mut(name).unwrap() } @@ -232,7 +233,8 @@ impl SignalChain { return Err(SignalChainError::EmptyName); } if !self.rooms.contains_key(name) { - self.rooms.insert(name.to_string(), Room::with_dial(name, self.global_dial)); + self.rooms + .insert(name.to_string(), Room::with_dial(name, self.global_dial)); } Ok(self.rooms.get_mut(name).unwrap()) } @@ -261,7 +263,8 @@ impl SignalChain { /// ``` pub fn room_with_dial(&mut self, name: &str, dial: Dial) -> &mut Room { if !self.rooms.contains_key(name) { - self.rooms.insert(name.to_string(), Room::with_dial(name, dial)); + self.rooms + .insert(name.to_string(), Room::with_dial(name, dial)); } self.rooms.get_mut(name).unwrap() } @@ -322,7 +325,9 @@ impl SignalChain { /// assert_eq!(snaps.len(), 1); /// ``` pub fn cascade_from(&mut self, origin: &str, depth: usize) { - if depth == 0 { return; } + if depth == 0 { + return; + } // Extract top-2 inferences from origin, sorted by confidence descending. let top: Vec<(serde_json::Value, f64)> = { @@ -330,20 +335,28 @@ impl SignalChain { Some(r) => r, None => return, }; - let mut sorted: Vec<&Inference> = room.inferences.iter() + let mut sorted: Vec<&Inference> = room + .inferences + .iter() .filter(|inf| inf.confidence > 0.5) .collect(); sorted.sort_by(|a, b| { - b.confidence.partial_cmp(&a.confidence).unwrap_or(std::cmp::Ordering::Equal) + b.confidence + .partial_cmp(&a.confidence) + .unwrap_or(std::cmp::Ordering::Equal) }); - sorted.into_iter().take(2) + sorted + .into_iter() + .take(2) .map(|inf| (inf.hypothesis.clone(), inf.confidence)) .collect() }; // Inject into every sibling room in the chain. for (name, room) in &mut self.rooms { - if name == origin { continue; } + if name == origin { + continue; + } for (hypothesis, confidence) in &top { room.add_snap(hypothesis.clone(), confidence * 0.8); } @@ -401,7 +414,13 @@ mod tests { chain.room_with_dial("hard-room", Dial::hard()); chain.room_with_dial("soft-room", Dial::soft()); - assert_eq!(chain.rooms.get("hard-room").unwrap().dial_position.position, 0.0); - assert_eq!(chain.rooms.get("soft-room").unwrap().dial_position.position, 1.0); + assert_eq!( + chain.rooms.get("hard-room").unwrap().dial_position.position, + 0.0 + ); + assert_eq!( + chain.rooms.get("soft-room").unwrap().dial_position.position, + 1.0 + ); } } diff --git a/crates/openshell-signal-chain/src/snap.rs b/crates/openshell-signal-chain/src/snap.rs index b0e4fcb852..58017b3a28 100644 --- a/crates/openshell-signal-chain/src/snap.rs +++ b/crates/openshell-signal-chain/src/snap.rs @@ -23,8 +23,8 @@ //! assert_eq!(snap.confidence, 1.0); //! ``` -use serde::{Deserialize, Serialize}; use chrono::{DateTime, Utc}; +use serde::{Deserialize, Serialize}; /// A hard-locked fact in a room. /// diff --git a/crates/openshell-signal-chain/src/spline_connector.rs b/crates/openshell-signal-chain/src/spline_connector.rs index b267271ea8..9d133e7868 100644 --- a/crates/openshell-signal-chain/src/spline_connector.rs +++ b/crates/openshell-signal-chain/src/spline_connector.rs @@ -17,8 +17,8 @@ use serde::{Deserialize, Serialize}; use crate::{ - Dial, Inference, SignalChain, constraint::{SplineConstraint, ViolationSeverity}, + Dial, Inference, SignalChain, }; /// Inference level — discrete categories of reasoning depth. @@ -49,13 +49,13 @@ impl InferenceLevel { pub fn from_dial(dial: Dial) -> Self { match dial.position { p if p <= 0.05 => InferenceLevel::Formal, - p if p <= 0.2 => InferenceLevel::Bathy, + p if p <= 0.2 => InferenceLevel::Bathy, p if p <= 0.35 => InferenceLevel::Commit, - p if p <= 0.5 => InferenceLevel::Analysis, + p if p <= 0.5 => InferenceLevel::Analysis, p if p <= 0.65 => InferenceLevel::Review, - p if p <= 0.8 => InferenceLevel::Extrapolate, + p if p <= 0.8 => InferenceLevel::Extrapolate, p if p <= 0.95 => InferenceLevel::Creative, - _ => InferenceLevel::Exploratory, + _ => InferenceLevel::Exploratory, } } @@ -120,7 +120,12 @@ impl SplineRoom { } } - fn evaluate_single(&self, constraint: &SplineConstraint, value: i32, dial: Dial) -> SplineEvaluation { + fn evaluate_single( + &self, + constraint: &SplineConstraint, + value: i32, + dial: Dial, + ) -> SplineEvaluation { match constraint.evaluate(value, dial) { Ok(curvature) => SplineEvaluation { constraint_name: constraint.name.clone(), @@ -222,19 +227,29 @@ impl SplineChain { pub fn spline_room(&mut self, name: &str, dial: Dial) -> &mut SplineRoom { if !self.spline_rooms.contains_key(name) { - self.spline_rooms.insert(name.to_string(), SplineRoom::new(name, dial)); + self.spline_rooms + .insert(name.to_string(), SplineRoom::new(name, dial)); } self.spline_rooms.get_mut(name).unwrap() } - pub fn add_spline_room(&mut self, name: &str, dial: Dial, constraints: Vec) -> &mut SplineRoom { + pub fn add_spline_room( + &mut self, + name: &str, + dial: Dial, + constraints: Vec, + ) -> &mut SplineRoom { let mut room = SplineRoom::new(name, dial); room.add_constraints(constraints); self.spline_rooms.insert(name.to_string(), room); self.spline_rooms.get_mut(name).unwrap() } - pub fn query_spline_room(&self, name: &str, override_dial: Option) -> Option> { + pub fn query_spline_room( + &self, + name: &str, + override_dial: Option, + ) -> Option> { self.spline_rooms.get(name).map(|r| r.query(override_dial)) } @@ -245,7 +260,11 @@ impl SplineChain { .collect() } - pub fn push(&mut self, room_name: &str, values: impl IntoIterator) -> Option> { + pub fn push( + &mut self, + room_name: &str, + values: impl IntoIterator, + ) -> Option> { self.spline_rooms.get_mut(room_name).map(|r| { r.push_values(values); r.query(None) @@ -299,7 +318,12 @@ mod tests { ]; for (level, expected_pos) in levels { let dial = level.to_dial(); - assert!((dial.position - expected_pos).abs() < 0.01, "level {:?} got {:?}", level, dial.position); + assert!( + (dial.position - expected_pos).abs() < 0.01, + "level {:?} got {:?}", + level, + dial.position + ); let round_trip = InferenceLevel::from_dial(dial); assert_eq!(level, round_trip); } diff --git a/crates/openshell-signal-chain/tests/test_signal_chain.rs b/crates/openshell-signal-chain/tests/test_signal_chain.rs index ca9d107101..326a2a5192 100644 --- a/crates/openshell-signal-chain/tests/test_signal_chain.rs +++ b/crates/openshell-signal-chain/tests/test_signal_chain.rs @@ -3,7 +3,9 @@ //! Integration tests for signal-chain. -use openshell_signal_chain::{Dial, Room, SignalChain, DIAL_FORMAL, DIAL_ANALYSIS, SignalChainError}; +use openshell_signal_chain::{ + Dial, Room, SignalChain, SignalChainError, DIAL_ANALYSIS, DIAL_FORMAL, +}; #[test] fn test_dial_presets() { @@ -41,13 +43,22 @@ fn test_signal_chain() { // Drone mapping room let drone = chain.room("drone-salvage"); - drone.add_snap(serde_json::json!({"lat": 45.3, "lon": -122.8, "depth": 87.2}), 1.0); - drone.add_inference(serde_json::json!({"hypothesis": "possible anchor at 45.5, -123.0"}), 0.6); + drone.add_snap( + serde_json::json!({"lat": 45.3, "lon": -122.8, "depth": 87.2}), + 1.0, + ); + drone.add_inference( + serde_json::json!({"hypothesis": "possible anchor at 45.5, -123.0"}), + 0.6, + ); // Formal analysis room chain.room_with_dial("formal-proof", Dial::hard()); let formal = chain.room("formal-proof"); - formal.add_snap(serde_json::json!({"theorem": "H1_cohomology_detects_emergence"}), 1.0); + formal.add_snap( + serde_json::json!({"theorem": "H1_cohomology_detects_emergence"}), + 1.0, + ); // Query at different dials let all = chain.query_all(DIAL_ANALYSIS); @@ -58,7 +69,9 @@ fn test_signal_chain() { fn test_cascade() { // Two sibling rooms in a SignalChain (not parent→child nested hierarchy). let mut chain = SignalChain::new("test"); - chain.room("parent").add_inference(serde_json::json!({"idea": "from_parent"}), 0.8); + chain + .room("parent") + .add_inference(serde_json::json!({"idea": "from_parent"}), 0.8); // Child starts empty — cascade_from must prove propagation across siblings. chain.room("child"); @@ -67,7 +80,11 @@ fn test_cascade() { chain.cascade_from("parent", 1); let child = chain.get_room("child").unwrap(); - assert_eq!(child.snaps.len(), 1, "cascade_from should inject one snap into sibling"); + assert_eq!( + child.snaps.len(), + 1, + "cascade_from should inject one snap into sibling" + ); let expected = 0.8_f64 * 0.8; assert!( (child.snaps[0].confidence - expected).abs() < 1e-9, @@ -82,7 +99,8 @@ fn test_room_child_hierarchy() { room.add_inference(serde_json::json!({"level": "parent_inference"}), 0.7); // Add child - room.children.insert("child".to_string(), Room::new("child")); + room.children + .insert("child".to_string(), Room::new("child")); room.cascade(1); @@ -96,7 +114,9 @@ fn test_room_child_hierarchy() { fn test_empty_room_cascade() { // Room with no inferences cascading into children let mut parent = Room::new("empty-parent"); - parent.children.insert("child".to_string(), Room::new("child")); + parent + .children + .insert("child".to_string(), Room::new("child")); parent.cascade(1); @@ -167,7 +187,9 @@ fn test_cascade_sorts_by_confidence() { parent.add_inference(serde_json::json!({"b": 2}), 0.8); parent.add_inference(serde_json::json!({"noise": 0}), 0.2); // below 0.5, excluded - parent.children.insert("child".to_string(), Room::new("child")); + parent + .children + .insert("child".to_string(), Room::new("child")); parent.cascade(1); let child = parent.children.get("child").unwrap(); @@ -180,10 +202,22 @@ fn test_cascade_sorts_by_confidence() { #[test] fn test_try_new_rejects_invalid() { - assert!(matches!(Dial::try_new(-0.1), Err(SignalChainError::InvalidDial(_)))); - assert!(matches!(Dial::try_new(1.1), Err(SignalChainError::InvalidDial(_)))); - assert!(matches!(Dial::try_new(f64::NAN), Err(SignalChainError::InvalidDial(_)))); - assert!(matches!(Dial::try_new(f64::INFINITY), Err(SignalChainError::InvalidDial(_)))); + assert!(matches!( + Dial::try_new(-0.1), + Err(SignalChainError::InvalidDial(_)) + )); + assert!(matches!( + Dial::try_new(1.1), + Err(SignalChainError::InvalidDial(_)) + )); + assert!(matches!( + Dial::try_new(f64::NAN), + Err(SignalChainError::InvalidDial(_)) + )); + assert!(matches!( + Dial::try_new(f64::INFINITY), + Err(SignalChainError::InvalidDial(_)) + )); } #[test]