diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 7a8b2d374..bb27ee496 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -26,11 +26,65 @@ jobs: runs-on: ${{ matrix.os }} name: Run test suite - if: github.repository == 'quarto-dev/q2' - + + # This job compiles and runs PR-controlled code — build scripts and proc + # macros execute arbitrary code at build time — so it takes the least token + # that does the work. Nothing here writes to the repository: the only + # post-checkout git use is `git ls-files` / `git log` in the mtime step, + # both purely local. + permissions: + contents: read + + # Every `run:` step works inside the q2 checkout; the sibling repos below + # are checked out beside it, which is the layout the path-deps expect. + defaults: + run: + working-directory: q2 + steps: - name: Checkout Repo uses: actions/checkout@v6 + with: + path: q2 + # `actions/checkout`'s default is `true`, which leaves the token in a + # git credential file the compiled PR code could reach. No step needs + # it after the clone. + persist-credentials: false + + # Sibling checkouts — REQUIRED, not optional. This workspace does not + # resolve without them, so `cargo metadata` (and therefore every cargo + # command, including the fmt gate) fails before it starts: + # + # crates/cockpit-server -> ../../../openstreetmap-website-rs (osm-soa-bake) + # crates/stubs/q2-ndarray -> ../../../../ndarray (optional dep; + # cargo still resolves the path) + # [patch."…/lance-graph"] -> ../lance-graph/crates/lance-graph-contract + # [patch."…/OGAR"] -> ../OGAR/crates/{ogar-vocab,ogar-class-view, + # ogar-ontology,ogar-adapter-surrealql} + # + # All four are public, so the default token clones them — the same + # pattern bake-osm.yml already uses for ndarray + lance-graph. Each takes + # its own default branch (note ndarray's is `master`, not `main`). + - uses: actions/checkout@v6 + with: + repository: AdaWorldAPI/openstreetmap-website-rs + path: openstreetmap-website-rs + persist-credentials: false + - uses: actions/checkout@v6 + with: + repository: AdaWorldAPI/ndarray + path: ndarray + persist-credentials: false + - uses: actions/checkout@v6 + with: + repository: AdaWorldAPI/lance-graph + path: lance-graph + persist-credentials: false + - uses: actions/checkout@v6 + with: + repository: AdaWorldAPI/OGAR + path: OGAR + persist-credentials: false # Fix mtimes IMMEDIATELY after checkout, before anything else - name: Restore file modification times @@ -67,6 +121,8 @@ jobs: # action's prebuilts each run, which is cheap (~1-2s). cache-bin: false shared-key: "rust-test-suite" + # The workspace is no longer at the repo root — see `path: q2` above. + workspaces: q2 # Cache cargo-nextest and insta separately to avoid reinstalling - name: Install nextest @@ -119,6 +175,23 @@ jobs: if: runner.os == 'macOS' run: brew install minisign + # `lance-encoding` (pulled in transitively by the cockpit-server -> + # lance/lancedb tree) compiles `.proto` files in its build script, so + # `protoc` has to exist before any cargo command that builds it. On Debian + # `protobuf-compiler` ships the binary but NOT the well-known includes + # (`google/protobuf/*.proto`); `libprotobuf-dev` carries those, and + # without it the build fails on a missing import rather than a missing + # binary. Both packages, deliberately. + - name: Set up protoc (Linux) + if: runner.os == 'Linux' + shell: bash + run: sudo apt-get install -y protobuf-compiler libprotobuf-dev + + - name: Set up protoc (macOS) + if: runner.os == 'macOS' + shell: bash + run: brew install protobuf + # Free disk space on Linux runners (14 GB SSD is tight for Rust monorepo). # `remove_tool_cache: true` is safe — no step in this job uses /opt/hostedtoolcache/ # (no setup-node, setup-python, etc.). See claude-notes/2026-04-28-ci-disk-space-and-profile-ci.md. @@ -141,9 +214,29 @@ jobs: # Formatting gate (before build to fail fast). rustfmt and clippy are # orthogonal — clippy can pass on code rustfmt would rewrite — so a fmt # check is needed to stop external PRs landing unformatted code. No - # `rustfmt.toml` in-tree → nightly defaults (the toolchain set up above), - # matching the local `cargo fmt` post-edit hook. Linux-only: formatting is - # platform-independent, so running it once is enough. + # `rustfmt.toml` in-tree → the defaults of whichever rustfmt runs, and + # that is the one `rust-toolchain.toml` pins (1.98.1), NOT the nightly the + # step above installs: `dtolnay/rust-toolchain` only runs `rustup toolchain + # install` + `rustup default` and never exports `RUSTUP_TOOLCHAIN`, so the + # override file wins for every cargo invocation inside the checkout. That + # also matches the local `cargo fmt` post-edit hook. Linux-only: + # formatting is platform-independent, so running it once is enough. + # + # Measured, and worth knowing before it surprises someone: with the + # siblings present, `--all` reformats their sources too, so a sibling repo + # has to be rustfmt-clean for this gate to pass. Confirmed for BOTH a + # direct path-dep (`openstreetmap-website-rs/src/{chains,codebook}.rs`) + # and a crate reached through the sibling checkout + # (`lance-graph/crates/lance-graph-ogar/src/bridges/*.rs`, which turned + # this gate red on 2026-09-06 the moment an unformatted commit landed on + # lance-graph main). + # + # An earlier version of this comment claimed `--all` does NOT reach + # `[patch]`-replaced sources, generalising from one observation + # (`lance-graph-contract` carries drift and produced no diff). The + # lance-graph-ogar failure disproves the general form. What actually + # separates the two cases is not established — treat any sibling source + # as in scope until it is. - name: Rustfmt (check) if: runner.os == 'Linux' shell: bash diff --git a/cpic/src/bin/ingest.rs b/cpic/src/bin/ingest.rs index 8434c357e..2f98b4b8e 100644 --- a/cpic/src/bin/ingest.rs +++ b/cpic/src/bin/ingest.rs @@ -53,7 +53,13 @@ impl Graph { let c = self.counters.entry((n.classid, family)).or_insert(0); *c += 1; let identity = mint_identity(*c); - let g = NodeGuid::mint(n.classid, cascade3(&n.part), cascade3(&n.isa), family, identity); + let g = NodeGuid::mint( + n.classid, + cascade3(&n.part), + cascade3(&n.isa), + family, + identity, + ); self.minted += 1; if let Some(prev) = self.keys.insert(g.key16(), n.label.clone()) { eprintln!("[COLLISION] {} :: {prev} vs {}", g.hex(), n.label); @@ -72,7 +78,8 @@ impl Graph { g } fn edge(&mut self, src: NodeGuid, rel: &str, dst: NodeGuid) { - self.edges.push(format!("{}\t{rel}\t{}", src.hex(), dst.hex())); + self.edges + .push(format!("{}\t{rel}\t{}", src.hex(), dst.hex())); } } @@ -335,7 +342,10 @@ fn main() { fs::write(format!("{out}/pgx_edges.tsv"), eb).unwrap(); // ── summary + the "both axes route" demonstration ── - eprintln!("[ingest] minted {} GUIDs, {} collisions", gr.minted, gr.collisions); + eprintln!( + "[ingest] minted {} GUIDs, {} collisions", + gr.minted, gr.collisions + ); eprintln!( "[ingest] nodes recorded {} (diplotypes minted {}, recorded ≤{}), edges {}", gr.nodes.len(), @@ -355,7 +365,10 @@ fn main() { ("CYP2D6", "*4"), ]; eprintln!("\n[ingest] (part_of:is_a) cascade demo — HEEL/HIP/TWIG = (part·is_a):"); - eprintln!(" {:<14} {:<46} part_of is_a", "allele", "guid"); + eprintln!( + " {:<14} {:<46} part_of is_a", + "allele", "guid" + ); for (sym, name) in demo { if let Some(g) = allele_g.get(&(sym.to_string(), name.to_string())) { eprintln!( @@ -370,6 +383,8 @@ fn main() { } } eprintln!(" ↑ part_of HIGH bytes c3/ec/f7 = pharmacogenome→CYP→CYP2, shared by all four (siblings route together)."); - eprintln!(" is_a LOW bytes 58=allele, then function: no_function=6f, decreased=d8, normal=07."); + eprintln!( + " is_a LOW bytes 58=allele, then function: no_function=6f, decreased=d8, normal=07." + ); eprintln!(" CYP2C19*2 & CYP2D6*4 (both no-function, both CYP2) share the FULL path c358-ec6f-f76f — only family (gene) differs."); } diff --git a/cpic/src/bin/reason.rs b/cpic/src/bin/reason.rs index 4611c3d1e..37eea508c 100644 --- a/cpic/src/bin/reason.rs +++ b/cpic/src/bin/reason.rs @@ -38,7 +38,10 @@ fn print_outcome(o: &Outcome) { if let (Some(class), Some(level)) = (&o.classification, &o.cpic_level) { println!(" │ recommends (class={class}, cpic level {level})"); } - println!("\n ⊢ NARS deduction {} {} → recommendation", o.gene, o.input); + println!( + "\n ⊢ NARS deduction {} {} → recommendation", + o.gene, o.input + ); println!( " truth f={:.3} c={:.3} (expectation {:.3})", o.truth_f, o.truth_c, o.truth_exp diff --git a/cpic/src/bin/scan.rs b/cpic/src/bin/scan.rs index 857f3e588..07afe3a9f 100644 --- a/cpic/src/bin/scan.rs +++ b/cpic/src/bin/scan.rs @@ -14,7 +14,7 @@ //! //! Usage: scan [n_patients=1000000] [reps=5] -use cpic::{basin, cascade3, gene_part_of, norm, NodeGuid, GOLDEN32, CID_PHENOTYPE}; +use cpic::{basin, cascade3, gene_part_of, norm, NodeGuid, CID_PHENOTYPE, GOLDEN32}; use serde_json::Value; use std::fs; use std::time::Instant; @@ -105,7 +105,10 @@ fn main() { .find(|p| p.label == "CYP2C19 Poor Metabolizer") .unwrap_or(&phenos[0]); let tgt_prefix = target.prefix13; - let (g, r) = target.label.split_once(' ').unwrap_or((target.label.as_str(), "")); + let (g, r) = target + .label + .split_once(' ') + .unwrap_or((target.label.as_str(), "")); let marker = format!("{g}|{r}|"); println!( diff --git a/cpic/src/lib.rs b/cpic/src/lib.rs index 24a6deb13..4a2db0425 100644 --- a/cpic/src/lib.rs +++ b/cpic/src/lib.rs @@ -194,7 +194,10 @@ pub struct Truth { } impl Truth { pub fn deduction(self, o: Truth) -> Truth { - Truth { f: self.f * o.f, c: self.f * o.f * self.c * o.c } + Truth { + f: self.f * o.f, + c: self.f * o.f * self.c * o.c, + } } pub fn exp(self) -> f32 { self.c * (self.f - 0.5) + 0.5 @@ -248,7 +251,7 @@ pub struct Kb { recs: Vec, drug_by_name: HashMap, // lc name -> (drugid, display name) pair_level: HashMap<(String, String), String>, // (gene, drugid) -> cpiclevel - pair_gid: HashMap<(String, String), i64>, // (gene, drugid) -> guidelineid + pair_gid: HashMap<(String, String), i64>, // (gene, drugid) -> guidelineid guideline: HashMap, usize)>, // gid -> (notesonusage, gene-count) } @@ -412,8 +415,15 @@ fn new_outcome(gene: &str, input: &str, drug: &str) -> Outcome { /// Resolve the 2nd arg to a phenotype + the t1 (input→phenotype) truth. A direct phenotype is /// near-certain; a diplotype is combined by the transparent simple allele-function rule (lower c). fn resolve_phenotype(kb: &Kb, gene: &str, input: &str) -> Option<(String, Truth, String)> { - if kb.gene_results.contains(&(gene.to_string(), input.to_string())) { - return Some((input.to_string(), Truth { f: 1.0, c: 0.99 }, "direct phenotype".into())); + if kb + .gene_results + .contains(&(gene.to_string(), input.to_string())) + { + return Some(( + input.to_string(), + Truth { f: 1.0, c: 0.99 }, + "direct phenotype".into(), + )); } let alleles: Vec<&str> = input.split('/').collect(); if alleles.len() != 2 { @@ -421,20 +431,34 @@ fn resolve_phenotype(kb: &Kb, gene: &str, input: &str) -> Option<(String, Truth, } let mut score = 0.0; for al in &alleles { - let st = kb.allele_status.get(&(gene.to_string(), al.trim().to_string()))?; + let st = kb + .allele_status + .get(&(gene.to_string(), al.trim().to_string()))?; score += rank(st)?; } let class = class_from_score(score); let cands = [ format!("{class} Metabolizer"), format!("{class} Function"), - if score == 0.5 { "Decreased Function".to_string() } else { String::new() }, + if score == 0.5 { + "Decreased Function".to_string() + } else { + String::new() + }, ]; let pheno = cands .iter() .find(|c| !c.is_empty() && kb.gene_results.contains(&(gene.to_string(), (*c).clone())))?; - let c = if alleles[0].trim() == alleles[1].trim() { 0.85 } else { 0.7 }; - Some((pheno.clone(), Truth { f: 1.0, c }, format!("simple rule (score {score})"))) + let c = if alleles[0].trim() == alleles[1].trim() { + 0.85 + } else { + 0.7 + }; + Some(( + pheno.clone(), + Truth { f: 1.0, c }, + format!("simple rule (score {score})"), + )) } /// Reason a `{gene, diplotype|phenotype, drug}` scenario over the real CPIC graph → `Outcome`. @@ -444,7 +468,8 @@ pub fn reason(kb: &Kb, gene: &str, input: &str, drug: &str) -> Outcome { let mut o = new_outcome(gene, input, drug); let Some((drugid, _drugname)) = kb.drug_by_name.get(&drug.to_lowercase()).cloned() else { - o.flags.push(format!("drug '{drug}' is not in the CPIC drug table")); + o.flags + .push(format!("drug '{drug}' is not in the CPIC drug table")); return o; }; let Some((pheno, t1, how)) = resolve_phenotype(kb, gene, input) else { @@ -488,11 +513,13 @@ pub fn reason(kb: &Kb, gene: &str, input: &str, drug: &str) -> Outcome { }); let Some(rec) = rec else { - o.flags - .push(format!("no simple phenotype→recommendation for {gene} {pheno} + {drug}")); + o.flags.push(format!( + "no simple phenotype→recommendation for {gene} {pheno} + {drug}" + )); if let Some(gid) = kb.pair_gid.get(&(gene.to_string(), drugid.clone())) { if let Some((Some(notes), _)) = kb.guideline.get(gid) { - o.flags.push(format!("COMPLEX guideline g{gid} (CPIC): {notes}")); + o.flags + .push(format!("COMPLEX guideline g{gid} (CPIC): {notes}")); } } return o; @@ -520,14 +547,22 @@ pub fn reason(kb: &Kb, gene: &str, input: &str, drug: &str) -> Outcome { "D" => 0.45, _ => 0.7, }; - let t = t1.deduction(Truth { f: class_f, c: level_c }); + let t = t1.deduction(Truth { + f: class_f, + c: level_c, + }); { let p = vec!["recommendations".into(), format!("g{gid}"), norm(&drugid)]; o.chain.push(ChainNode { role: "recommendation".into(), label: format!("g{gid} → {class}"), - guid: addr(CID_REC, &p, &["recommendation".into(), norm(&class)], &format!("rec:g{gid}")), + guid: addr( + CID_REC, + &p, + &["recommendation".into(), norm(&class)], + &format!("rec:g{gid}"), + ), }); } @@ -544,8 +579,9 @@ pub fn reason(kb: &Kb, gene: &str, input: &str, drug: &str) -> Outcome { o.flags.push(format!("COMPLEX guideline (CPIC note): {n}")); } if *ngenes > 1 { - o.flags - .push(format!("MULTI-GENE guideline ({ngenes} genes) — single-gene deduction is partial")); + o.flags.push(format!( + "MULTI-GENE guideline ({ngenes} genes) — single-gene deduction is partial" + )); } } o @@ -567,13 +603,20 @@ mod reason_tests { assert_eq!(o.classification.as_deref(), Some("Strong")); assert!(o.truth_c > 0.0 && o.truth_f > 0.0); assert_eq!(o.chain.len(), 3); // diplotype, phenotype, recommendation - assert!(o.recommendation.unwrap().to_lowercase().contains("clopidogrel")); + assert!(o + .recommendation + .unwrap() + .to_lowercase() + .contains("clopidogrel")); } #[test] fn unknown_drug_is_flagged_not_fabricated() { let o = reason(&kb(), "CYP2C19", "*2/*2", "definitely_not_a_drug"); assert!(!o.resolved); - assert!(o.flags.iter().any(|f| f.contains("not in the CPIC drug table"))); + assert!(o + .flags + .iter() + .any(|f| f.contains("not in the CPIC drug table"))); } } diff --git a/crates/aiwar-ingest/src/encounter_round.rs b/crates/aiwar-ingest/src/encounter_round.rs index 03dd39ffa..e0ae0c448 100644 --- a/crates/aiwar-ingest/src/encounter_round.rs +++ b/crates/aiwar-ingest/src/encounter_round.rs @@ -391,7 +391,10 @@ mod tests { // endpoints — the edge is skipped instead. let cypher = "MERGE (a)-[r:PERSON_LINK]->(b)"; let (_, edges) = parse_cypher(cypher); - assert!(edges.is_empty(), "unbound endpoints are skipped, not emitted"); + assert!( + edges.is_empty(), + "unbound endpoints are skipped, not emitted" + ); } #[test] @@ -405,9 +408,11 @@ mod tests { assert_eq!(edges[0].source, "Development"); assert_eq!(edges[0].target, "currentStatus_airo"); assert_eq!(edges[0].rel_type, "VALID_FOR"); - assert!(nodes - .iter() - .any(|n| n.id == "Development" && n.labels == ["SchemaValue"])); + assert!( + nodes + .iter() + .any(|n| n.id == "Development" && n.labels == ["SchemaValue"]) + ); assert!(nodes.iter().any(|n| n.id == "currentStatus_airo")); // no phantom single-letter node ids assert!(!nodes.iter().any(|n| n.id == "v" || n.id == "a")); @@ -438,10 +443,7 @@ mod tests { ); assert_eq!(full_edges[0].target, "Hegseth"); assert_eq!(full_edges[0].rel_type, "PERSON_LINK"); - assert_eq!( - &full_edges[0].properties["label"], - "appointed SecDef" - ); + assert_eq!(&full_edges[0].properties["label"], "appointed SecDef"); } #[test] diff --git a/crates/aiwar-ingest/src/lib.rs b/crates/aiwar-ingest/src/lib.rs index 496a89b47..54f5c2c2b 100644 --- a/crates/aiwar-ingest/src/lib.rs +++ b/crates/aiwar-ingest/src/lib.rs @@ -405,10 +405,7 @@ mod tests { assert_eq!(maven.node_type, "System"); assert_eq!(maven.label, "Project Maven"); // non id/name fields preserved verbatim - assert_eq!( - &maven.properties["year"], - &serde_json::json!(2017) - ); + assert_eq!(&maven.properties["year"], &serde_json::json!(2017)); assert_eq!( &maven.properties["MLTask"], &serde_json::json!("object-detection") @@ -432,10 +429,7 @@ mod tests { assert_eq!(dev.source, "Maven"); assert_eq!(dev.target, "Palantir"); assert_eq!(dev.weight, 3.0); - assert_eq!( - &dev.properties["label"], - &serde_json::json!("builds") - ); + assert_eq!(&dev.properties["label"], &serde_json::json!("builds")); assert!( g.edges @@ -538,7 +532,11 @@ mod tests { }"#; let g = load_from_str(json).expect("non-finite literals sanitized to null"); assert_eq!(g.node_count(), 1); - let x = g.nodes.iter().find(|n| n.id == "X").expect("node X present"); + let x = g + .nodes + .iter() + .find(|n| n.id == "X") + .expect("node X present"); assert_eq!(x.label, "Sys X"); // the non-finite numeric became JSON null (or was dropped) — not a parse error. assert!(matches!( diff --git a/crates/neural-debug/src/diagnosis.rs b/crates/neural-debug/src/diagnosis.rs index 193a59d59..ecbbe3f01 100644 --- a/crates/neural-debug/src/diagnosis.rs +++ b/crates/neural-debug/src/diagnosis.rs @@ -4,8 +4,8 @@ use std::collections::{HashMap, HashSet}; use serde::{Deserialize, Serialize}; -use crate::registry::{FunctionMeta, ModuleSummary, NeuronState}; use crate::instrument; +use crate::registry::{FunctionMeta, ModuleSummary, NeuronState}; /// Diagnosis for a single function. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -58,7 +58,10 @@ impl NeuralDiagnosis { NeuronState::Static }; - module_map.entry(func.module.clone()).or_default().push(state); + module_map + .entry(func.module.clone()) + .or_default() + .push(state); neurons.push(NeuronDiagnosis { id: func.id.clone(), @@ -81,21 +84,46 @@ impl NeuralDiagnosis { let nan = states.iter().filter(|s| **s == NeuronState::Nan).count(); let stat = states.iter().filter(|s| **s == NeuronState::Static).count(); let stub = states.iter().filter(|s| **s == NeuronState::Stub).count(); - let wired = states.iter().filter(|s| **s == NeuronState::WiredUnused).count(); - ModuleSummary { name, total, alive, dead, nan, r#static: stat, stub, wired_unused: wired } + let wired = states + .iter() + .filter(|s| **s == NeuronState::WiredUnused) + .count(); + ModuleSummary { + name, + total, + alive, + dead, + nan, + r#static: stat, + stub, + wired_unused: wired, + } }) .collect(); let total_functions = neurons.len(); - let alive_count = neurons.iter().filter(|n| n.state == NeuronState::Alive).count(); - let dead_count = neurons.iter().filter(|n| n.state == NeuronState::Dead).count(); - let nan_count = neurons.iter().filter(|n| n.state == NeuronState::Nan).count(); + let alive_count = neurons + .iter() + .filter(|n| n.state == NeuronState::Alive) + .count(); + let dead_count = neurons + .iter() + .filter(|n| n.state == NeuronState::Dead) + .count(); + let nan_count = neurons + .iter() + .filter(|n| n.state == NeuronState::Nan) + .count(); NeuralDiagnosis { neurons, modules, total_functions, - alive_pct: if total_functions > 0 { alive_count as f32 / total_functions as f32 * 100.0 } else { 0.0 }, + alive_pct: if total_functions > 0 { + alive_count as f32 / total_functions as f32 * 100.0 + } else { + 0.0 + }, dead_count, nan_count, } diff --git a/crates/neural-debug/src/instrument.rs b/crates/neural-debug/src/instrument.rs index 4b9acf118..b85d2323d 100644 --- a/crates/neural-debug/src/instrument.rs +++ b/crates/neural-debug/src/instrument.rs @@ -50,7 +50,9 @@ fn counters() -> parking_lot::MutexGuard<'static, Option> { #[inline] pub fn track(_function_id: &str, f: impl FnOnce() -> T) -> T { #[cfg(not(feature = "instrument"))] - { f() } + { + f() + } #[cfg(feature = "instrument")] { @@ -61,8 +63,12 @@ pub fn track(_function_id: &str, f: impl FnOnce() -> T) -> T { let mut c = counters(); let c = c.as_mut().unwrap(); *c.calls.entry(function_id.to_string()).or_insert(0) += 1; - c.timing.entry(function_id.to_string()).or_default().push(elapsed); - c.last_called.insert(function_id.to_string(), Instant::now()); + c.timing + .entry(function_id.to_string()) + .or_default() + .push(elapsed); + c.last_called + .insert(function_id.to_string(), Instant::now()); result } @@ -72,7 +78,9 @@ pub fn track(_function_id: &str, f: impl FnOnce() -> T) -> T { #[inline] pub fn track_numeric(_function_id: &str, value: f32) -> f32 { #[cfg(not(feature = "instrument"))] - { value } + { + value + } #[cfg(feature = "instrument")] { @@ -81,13 +89,16 @@ pub fn track_numeric(_function_id: &str, value: f32) -> f32 { *c.calls.entry(function_id.to_string()).or_insert(0) += 1; if value.is_nan() || value.is_infinite() { - c.nan_producers.entry(function_id.to_string()).or_default().push(NanEvent { - timestamp_ms: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis() as u64, - input_snapshot: format!("returned {value}"), - }); + c.nan_producers + .entry(function_id.to_string()) + .or_default() + .push(NanEvent { + timestamp_ms: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64, + input_snapshot: format!("returned {value}"), + }); } value @@ -98,7 +109,9 @@ pub fn track_numeric(_function_id: &str, value: f32) -> f32 { #[inline] pub fn track_numeric_f64(_function_id: &str, value: f64) -> f64 { #[cfg(not(feature = "instrument"))] - { value } + { + value + } #[cfg(feature = "instrument")] { @@ -107,13 +120,16 @@ pub fn track_numeric_f64(_function_id: &str, value: f64) -> f64 { *c.calls.entry(function_id.to_string()).or_insert(0) += 1; if value.is_nan() || value.is_infinite() { - c.nan_producers.entry(function_id.to_string()).or_default().push(NanEvent { - timestamp_ms: std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis() as u64, - input_snapshot: format!("returned {value}"), - }); + c.nan_producers + .entry(function_id.to_string()) + .or_default() + .push(NanEvent { + timestamp_ms: std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64, + input_snapshot: format!("returned {value}"), + }); } value @@ -126,7 +142,11 @@ pub fn get_stats(function_id: &str) -> CallStats { let c = c.as_ref().unwrap(); let call_count = c.calls.get(function_id).copied().unwrap_or(0); let timings = c.timing.get(function_id); - let nan_count = c.nan_producers.get(function_id).map(|v| v.len()).unwrap_or(0); + let nan_count = c + .nan_producers + .get(function_id) + .map(|v| v.len()) + .unwrap_or(0); let (avg_us, max_us) = if let Some(ts) = timings { if ts.is_empty() { @@ -141,7 +161,10 @@ pub fn get_stats(function_id: &str) -> CallStats { (0.0, 0.0) }; - let last_called_ago_ms = c.last_called.get(function_id).map(|t| t.elapsed().as_millis() as u64); + let last_called_ago_ms = c + .last_called + .get(function_id) + .map(|t| t.elapsed().as_millis() as u64); CallStats { call_count, @@ -163,7 +186,10 @@ pub fn alive_functions() -> Vec { pub fn nan_functions() -> Vec<(String, Vec)> { let c = counters(); let c = c.as_ref().unwrap(); - c.nan_producers.iter().map(|(k, v)| (k.clone(), v.clone())).collect() + c.nan_producers + .iter() + .map(|(k, v)| (k.clone(), v.clone())) + .collect() } /// Reset all counters. diff --git a/crates/neural-debug/src/lib.rs b/crates/neural-debug/src/lib.rs index 29c440345..5f299cd12 100644 --- a/crates/neural-debug/src/lib.rs +++ b/crates/neural-debug/src/lib.rs @@ -12,14 +12,14 @@ //! MCP: /api/debug/* endpoints serve diagnosis to the cockpit //! ``` -pub mod registry; -pub mod instrument; pub mod diagnosis; -pub mod strategy_check; +pub mod instrument; +pub mod registry; #[cfg(feature = "mcp")] pub mod server; +pub mod strategy_check; -pub use registry::{FunctionMeta, NeuronState}; -pub use instrument::{track, track_numeric, CallStats}; pub use diagnosis::{NeuralDiagnosis, NeuronDiagnosis}; -pub use strategy_check::{StrategyDiagnosis, DepStatus, PipelineCheckResult}; +pub use instrument::{track, track_numeric, CallStats}; +pub use registry::{FunctionMeta, NeuronState}; +pub use strategy_check::{DepStatus, PipelineCheckResult, StrategyDiagnosis}; diff --git a/crates/neural-debug/src/registry.rs b/crates/neural-debug/src/registry.rs index f93b20e2a..a08fd5470 100644 --- a/crates/neural-debug/src/registry.rs +++ b/crates/neural-debug/src/registry.rs @@ -92,7 +92,9 @@ pub struct ModuleSummary { impl ModuleSummary { pub fn health_pct(&self) -> f32 { - if self.total == 0 { return 0.0; } + if self.total == 0 { + return 0.0; + } self.alive as f32 / self.total as f32 * 100.0 } } diff --git a/crates/neural-debug/src/strategy_check.rs b/crates/neural-debug/src/strategy_check.rs index 209d17294..2b6d00f7e 100644 --- a/crates/neural-debug/src/strategy_check.rs +++ b/crates/neural-debug/src/strategy_check.rs @@ -74,7 +74,10 @@ impl StrategyDiagnosis { } pub fn alive_deps(&self) -> usize { - self.deps.iter().filter(|d| d.status.is_operational()).count() + self.deps + .iter() + .filter(|d| d.status.is_operational()) + .count() } pub fn total_deps(&self) -> usize { @@ -153,7 +156,10 @@ pub fn default_pipeline_checks() -> Vec<(&'static str, Vec)> { ("Parse→Scan→Truth→Collapse", vec![0, 9, 11, 12]), ("Parse→DPJoin→Scan→Collapse", vec![0, 5, 9, 12]), ("Parse→Optimize→Scan→Collapse", vec![0, 7, 9, 12]), - ("Full pipeline (all 13)", vec![0, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14]), + ( + "Full pipeline (all 13)", + vec![0, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14], + ), ] } @@ -164,8 +170,14 @@ mod tests { #[test] fn test_dep_status_operational() { assert!(DepStatus::Alive.is_operational()); - assert!(!DepStatus::Dead { reason: "todo".into() }.is_operational()); - assert!(!DepStatus::Nan { input: "0.0".into() }.is_operational()); + assert!(!DepStatus::Dead { + reason: "todo".into() + } + .is_operational()); + assert!(!DepStatus::Nan { + input: "0.0".into() + } + .is_operational()); assert!(!DepStatus::Stub.is_operational()); } @@ -180,8 +192,22 @@ mod tests { strategy: "test".into(), strategy_index: 0, deps: vec![ - DepCheck { name: "a".into(), status: DepStatus::Alive, location: "a.rs:1".into(), error: None, latency_us: 100 }, - DepCheck { name: "b".into(), status: DepStatus::Dead { reason: "todo".into() }, location: "b.rs:1".into(), error: Some("todo".into()), latency_us: 0 }, + DepCheck { + name: "a".into(), + status: DepStatus::Alive, + location: "a.rs:1".into(), + error: None, + latency_us: 100, + }, + DepCheck { + name: "b".into(), + status: DepStatus::Dead { + reason: "todo".into(), + }, + location: "b.rs:1".into(), + error: Some("todo".into()), + latency_us: 0, + }, ], self_test: None, verdict: Verdict::Partial, diff --git a/crates/osint-bake/src/bin/body.rs b/crates/osint-bake/src/bin/body.rs index 4a81e1fe6..3899f5ba9 100644 --- a/crates/osint-bake/src/bin/body.rs +++ b/crates/osint-bake/src/bin/body.rs @@ -34,7 +34,7 @@ //! inflates it client-side. `cockpit/public/body.soa*` is gitignored so a local //! bake never lands the binary in the repo. -use lance_graph_contract::canonical_node::{classid_read_mode, NodeGuid}; +use lance_graph_contract::canonical_node::{NodeGuid, classid_read_mode}; use std::path::{Path, PathBuf}; /// FMA V3 cascade key (`0x0A01_1000` since the 2026-07-02 half-order flip; pre-flip @@ -57,7 +57,9 @@ const fn mixin_for_depth(k: usize) -> u8 { } fn arg(n: usize, default: &str) -> String { - std::env::args().nth(n).unwrap_or_else(|| default.to_string()) + std::env::args() + .nth(n) + .unwrap_or_else(|| default.to_string()) } fn main() { @@ -65,11 +67,13 @@ fn main() { let spm1_path = arg(2, "scratch-fma/out/body.spm1"); let out_arg = arg(3, ""); - let nodes_json = std::fs::read_to_string(&nodes_path) - .unwrap_or_else(|e| panic!("read {nodes_path}: {e}")); + let nodes_json = + std::fs::read_to_string(&nodes_path).unwrap_or_else(|e| panic!("read {nodes_path}: {e}")); let doc: serde_json::Value = serde_json::from_str(&nodes_json).unwrap_or_else(|e| panic!("parse {nodes_path}: {e}")); - let nodes = doc["nodes"].as_array().expect("body.nodes.json: .nodes array"); + let nodes = doc["nodes"] + .as_array() + .expect("body.nodes.json: .nodes array"); let spm1 = std::fs::read(&spm1_path).unwrap_or_else(|e| panic!("read {spm1_path}: {e}")); assert_eq!(&spm1[..4], b"SPM1", "geometry block is not SPM1"); @@ -85,20 +89,31 @@ fn main() { let depth = n["depth"].as_u64().unwrap_or(0).min(255) as u8; deepest = deepest.max(depth); let rgb = n["rgb"].as_array(); - let r = rgb.and_then(|a| a.first()).and_then(|v| v.as_u64()).unwrap_or(180) as u8; - let g = rgb.and_then(|a| a.get(1)).and_then(|v| v.as_u64()).unwrap_or(180) as u8; - let b = rgb.and_then(|a| a.get(2)).and_then(|v| v.as_u64()).unwrap_or(180) as u8; + let r = rgb + .and_then(|a| a.first()) + .and_then(|v| v.as_u64()) + .unwrap_or(180) as u8; + let g = rgb + .and_then(|a| a.get(1)) + .and_then(|v| v.as_u64()) + .unwrap_or(180) as u8; + let b = rgb + .and_then(|a| a.get(2)) + .and_then(|v| v.as_u64()) + .unwrap_or(180) as u8; let v_start = n["v_start"].as_u64().unwrap_or(0) as u32; let v_count = n["v_count"].as_u64().unwrap_or(0) as u32; // cascade = is_a ancestor sibling ranks root->self (≤5 tier identity bytes). let cascade = n["cascade"].as_array().cloned().unwrap_or_default(); - let id_at = |k: usize| -> u8 { - cascade.get(k).and_then(|v| v.as_u64()).unwrap_or(0) as u8 - }; + let id_at = |k: usize| -> u8 { cascade.get(k).and_then(|v| v.as_u64()).unwrap_or(0) as u8 }; let tier_at = |k: usize| -> u16 { let id = id_at(k); - if id == 0 { 0 } else { tier(mixin_for_depth(k), id) } + if id == 0 { + 0 + } else { + tier(mixin_for_depth(k), id) + } }; // Mint by the classid's registered tail variant (V3), never hardcoding the @@ -106,12 +121,12 @@ fn main() { let key = NodeGuid::mint_for( tail, CLASSID_FMA, - tier_at(0), // HEEL [depth0-mixin : rank] - tier_at(1), // HIP [depth1-mixin : rank] - tier_at(2), // TWIG [depth2-mixin : rank] - tier_at(3), // LEAF [depth3-mixin : rank] - u32::from(tier_at(4)), // family[depth4-mixin : rank] - row, // identity — stable concept row (node_row link) + tier_at(0), // HEEL [depth0-mixin : rank] + tier_at(1), // HIP [depth1-mixin : rank] + tier_at(2), // TWIG [depth2-mixin : rank] + tier_at(3), // LEAF [depth3-mixin : rank] + u32::from(tier_at(4)), // family[depth4-mixin : rank] + row, // identity — stable concept row (node_row link) ); table.extend_from_slice(key.as_bytes()); @@ -153,8 +168,15 @@ fn main() { std::fs::write(&out_path, &out).unwrap_or_else(|e| panic!("write {}: {e}", out_path.display())); println!("── body.soa (BSO1) ──"); - println!(" V3 substrate : {} concepts minted on CLASSID_FMA_V3 cascade (max depth {deepest})", nodes.len()); + println!( + " V3 substrate : {} concepts minted on CLASSID_FMA_V3 cascade (max depth {deepest})", + nodes.len() + ); println!(" geometry : {vc} verts · {tc} tris (ALL points, full-res SPM1)"); - println!(" node table : {} B geometry block: {} B", table.len(), spm1.len()); + println!( + " node table : {} B geometry block: {} B", + table.len(), + spm1.len() + ); println!(" baked {} ({} B)", out_path.display(), out.len()); } diff --git a/crates/osint-bake/src/morton.rs b/crates/osint-bake/src/morton.rs index 422c04c1d..1feaba56b 100644 --- a/crates/osint-bake/src/morton.rs +++ b/crates/osint-bake/src/morton.rs @@ -65,7 +65,14 @@ mod tests { #[test] fn round_trips() { - for &(x, y) in &[(0u16, 0u16), (1, 0), (0, 1), (3, 2), (255, 255), (40_000, 25_000)] { + for &(x, y) in &[ + (0u16, 0u16), + (1, 0), + (0, 1), + (3, 2), + (255, 255), + (40_000, 25_000), + ] { assert_eq!(decode(encode(x, y)), (x, y), "round-trip ({x},{y})"); } } @@ -96,6 +103,9 @@ mod tests { // horizontally-adjacent cells stay within one 4×4 block of each other. let a = encode(10, 10); let b = encode(11, 10); - assert!((a as i64 - b as i64).abs() <= 16, "adjacent cells stay near in Z-order"); + assert!( + (a as i64 - b as i64).abs() <= 16, + "adjacent cells stay near in Z-order" + ); } } diff --git a/crates/stubs/graph-flow/src/storage.rs b/crates/stubs/graph-flow/src/storage.rs index 351a629e3..13ef8df33 100644 --- a/crates/stubs/graph-flow/src/storage.rs +++ b/crates/stubs/graph-flow/src/storage.rs @@ -22,10 +22,13 @@ impl Default for Session { impl Session { pub fn new() -> Self { Self { - id: format!("session-{}", std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_millis()), + id: format!( + "session-{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_millis() + ), context: SessionContext::new(), current_node: String::new(), } @@ -78,7 +81,8 @@ impl SessionContext { /// Get a value from the context. Returns None if key doesn't exist or type doesn't match. pub async fn get(&self, key: &str) -> Option { let data = self.data.read().await; - data.get(key).and_then(|v| serde_json::from_value(v.clone()).ok()) + data.get(key) + .and_then(|v| serde_json::from_value(v.clone()).ok()) } /// Get raw JSON value. diff --git a/crates/stubs/graph-flow/src/thinking.rs b/crates/stubs/graph-flow/src/thinking.rs index a95e5901d..dabc0b351 100644 --- a/crates/stubs/graph-flow/src/thinking.rs +++ b/crates/stubs/graph-flow/src/thinking.rs @@ -28,43 +28,71 @@ struct Layer { /// Build the default 10-layer thinking graph. pub fn build_thinking_graph() -> ThinkingGraph { let layers = vec![ - Layer { name: "sensory_ingest".into(), execute: Box::new(|input| { - serde_json::json!({ "parsed": input, "tokens": input.split_whitespace().count() }) - })}, - Layer { name: "fingerprint".into(), execute: Box::new(|input| { - // Simple hash-based fingerprint - let hash: u64 = input.bytes().fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); - serde_json::json!({ "fingerprint": format!("{:016x}", hash) }) - })}, - Layer { name: "cascade_search".into(), execute: Box::new(|input| { - let is_familiar = input.contains("MATCH") || input.contains("RETURN"); - serde_json::json!({ "band": if is_familiar { "Foveal" } else { "Parafoveal" }, "familiar": is_familiar }) - })}, - Layer { name: "semiring_reasoning".into(), execute: Box::new(|_| { - serde_json::json!({ "inference": "deduction", "truth": { "f": 0.85, "c": 0.72 } }) - })}, - Layer { name: "memory_consolidation".into(), execute: Box::new(|_| { - serde_json::json!({ "seal": "staunen", "new_learning": true }) - })}, - Layer { name: "planning".into(), execute: Box::new(|input| { - serde_json::json!({ "strategy": if input.contains("*") { "depth_first" } else { "breadth_first" } }) - })}, - Layer { name: "action".into(), execute: Box::new(|input| { - serde_json::json!({ "executed": true, "query": input }) - })}, - Layer { name: "output".into(), execute: Box::new(|input| { - serde_json::json!({ "result": format!("Thinking complete for: {}", &input[..input.len().min(50)]) }) - })}, - Layer { name: "meta_cognition".into(), execute: Box::new(|_| { - serde_json::json!({ "reflection": "process was efficient", "confidence": 0.78 }) - })}, - Layer { name: "pet_scan".into(), execute: Box::new(|_| { - serde_json::json!({ "layers_executed": 10, "trace": [ - "sensory_ingest", "fingerprint", "cascade_search", - "semiring_reasoning", "memory_consolidation", "planning", - "action", "output", "meta_cognition", "pet_scan" - ]}) - })}, + Layer { + name: "sensory_ingest".into(), + execute: Box::new( + |input| serde_json::json!({ "parsed": input, "tokens": input.split_whitespace().count() }), + ), + }, + Layer { + name: "fingerprint".into(), + execute: Box::new(|input| { + // Simple hash-based fingerprint + let hash: u64 = input + .bytes() + .fold(0u64, |acc, b| acc.wrapping_mul(31).wrapping_add(b as u64)); + serde_json::json!({ "fingerprint": format!("{:016x}", hash) }) + }), + }, + Layer { + name: "cascade_search".into(), + execute: Box::new(|input| { + let is_familiar = input.contains("MATCH") || input.contains("RETURN"); + serde_json::json!({ "band": if is_familiar { "Foveal" } else { "Parafoveal" }, "familiar": is_familiar }) + }), + }, + Layer { + name: "semiring_reasoning".into(), + execute: Box::new( + |_| serde_json::json!({ "inference": "deduction", "truth": { "f": 0.85, "c": 0.72 } }), + ), + }, + Layer { + name: "memory_consolidation".into(), + execute: Box::new(|_| serde_json::json!({ "seal": "staunen", "new_learning": true })), + }, + Layer { + name: "planning".into(), + execute: Box::new( + |input| serde_json::json!({ "strategy": if input.contains("*") { "depth_first" } else { "breadth_first" } }), + ), + }, + Layer { + name: "action".into(), + execute: Box::new(|input| serde_json::json!({ "executed": true, "query": input })), + }, + Layer { + name: "output".into(), + execute: Box::new( + |input| serde_json::json!({ "result": format!("Thinking complete for: {}", &input[..input.len().min(50)]) }), + ), + }, + Layer { + name: "meta_cognition".into(), + execute: Box::new( + |_| serde_json::json!({ "reflection": "process was efficient", "confidence": 0.78 }), + ), + }, + Layer { + name: "pet_scan".into(), + execute: Box::new(|_| { + serde_json::json!({ "layers_executed": 10, "trace": [ + "sensory_ingest", "fingerprint", "cascade_search", + "semiring_reasoning", "memory_consolidation", "planning", + "action", "output", "meta_cognition", "pet_scan" + ]}) + }), + }, ]; ThinkingGraph { layers } } @@ -79,16 +107,21 @@ impl ThinkingGraph { let layer_idx = self.layers.iter().position(|l| l.name == current); let idx = match layer_idx { Some(i) => i, - None => return Ok(StepResult { - status: ExecutionStatus::Error(format!("Unknown layer: {}", current)), - node_name: current, - output: None, - }), + None => { + return Ok(StepResult { + status: ExecutionStatus::Error(format!("Unknown layer: {}", current)), + node_name: current, + output: None, + }) + } }; // Execute this layer let output = (self.layers[idx].execute)(&input); - session.context.set(&format!("layer_{}", current), output.clone()).await; + session + .context + .set(&format!("layer_{}", current), output.clone()) + .await; // Merge layer outputs into aggregate contexts if current == "cascade_search" { @@ -97,7 +130,10 @@ impl ThinkingGraph { } } if current == "memory_consolidation" { - let staunen = output.get("new_learning").and_then(|v| v.as_bool()).unwrap_or(false); + let staunen = output + .get("new_learning") + .and_then(|v| v.as_bool()) + .unwrap_or(false); session.context.set("staunen", staunen).await; } if current == "output" { @@ -135,7 +171,10 @@ mod tests { async fn test_thinking_graph_runs_all_layers() { let graph = build_thinking_graph(); let mut session = Session::new_from_task("test".into(), "sensory_ingest"); - session.context.set("raw_input", "MATCH (n:System) RETURN n.name".to_string()).await; + session + .context + .set("raw_input", "MATCH (n:System) RETURN n.name".to_string()) + .await; let mut steps = 0; loop { diff --git a/crates/stubs/notebook-query/src/analyst.rs b/crates/stubs/notebook-query/src/analyst.rs index 9f2f76940..79b4b29f9 100644 --- a/crates/stubs/notebook-query/src/analyst.rs +++ b/crates/stubs/notebook-query/src/analyst.rs @@ -5,7 +5,7 @@ //! relationships, runs NARS deduction/abduction/induction, builds //! causal chains, and projects forward. -use crate::reasoning::{infer_edges, TruthEdge, TruthValue}; +use crate::reasoning::{TruthEdge, TruthValue, infer_edges}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -22,8 +22,14 @@ pub enum AnalysisBucket { impl AnalysisBucket { pub fn all() -> &'static [AnalysisBucket] { - &[Self::EconomicReview, Self::CivilEngineering, Self::PoliticalDynamics, - Self::AiDevelopmentImpact, Self::KillChainAnalysis, Self::SurveillanceEcosystem] + &[ + Self::EconomicReview, + Self::CivilEngineering, + Self::PoliticalDynamics, + Self::AiDevelopmentImpact, + Self::KillChainAnalysis, + Self::SurveillanceEcosystem, + ] } pub fn label(&self) -> &'static str { match self { @@ -57,24 +63,43 @@ impl AnalysisBucket { Self::CivilEngineering => &["Claude", "Roomba", "Pokemon", "Niantic"], Self::PoliticalDynamics => &["US", "Israel", "NATO", "China", "Russia", "UK", "DIANA"], Self::AiDevelopmentImpact => &["Lattice", "Replicator", "AIP", "Gotham", "LLM"], - Self::KillChainAnalysis => &["Lavender", "Gospel", "Fire", "Daddy", "Alchemist", "Legion"], - Self::SurveillanceEcosystem => &["Pegasus", "Clearview", "Gotham", "Foundry", "Palantir", "Fortify"], + Self::KillChainAnalysis => { + &["Lavender", "Gospel", "Fire", "Daddy", "Alchemist", "Legion"] + } + Self::SurveillanceEcosystem => &[ + "Pegasus", + "Clearview", + "Gotham", + "Foundry", + "Palantir", + "Fortify", + ], } } } #[derive(Debug, Deserialize)] struct RawGraph { - #[serde(rename = "N_Systems", default)] systems: Vec, - #[serde(rename = "N_Stakeholders", default)] stakeholders: Vec, - #[serde(rename = "N_People", default)] people: Vec, - #[serde(rename = "N_Civic", default)] civic: Vec, - #[serde(rename = "N_Historical", default)] historical: Vec, - #[serde(rename = "E_connection", default)] e_connection: Vec, - #[serde(rename = "E_isDevelopedBy", default)] e_developed: Vec, - #[serde(rename = "E_isDeployedBy", default)] e_deployed: Vec, - #[serde(rename = "E_place", default)] e_place: Vec, - #[serde(rename = "E_people", default)] e_people: Vec, + #[serde(rename = "N_Systems", default)] + systems: Vec, + #[serde(rename = "N_Stakeholders", default)] + stakeholders: Vec, + #[serde(rename = "N_People", default)] + people: Vec, + #[serde(rename = "N_Civic", default)] + civic: Vec, + #[serde(rename = "N_Historical", default)] + historical: Vec, + #[serde(rename = "E_connection", default)] + e_connection: Vec, + #[serde(rename = "E_isDevelopedBy", default)] + e_developed: Vec, + #[serde(rename = "E_isDeployedBy", default)] + e_deployed: Vec, + #[serde(rename = "E_place", default)] + e_place: Vec, + #[serde(rename = "E_people", default)] + e_people: Vec, } #[derive(Debug, Deserialize)] @@ -90,7 +115,11 @@ struct RawNode { } #[derive(Debug, Deserialize)] -struct RawEdge { source: Option, target: Option, label: Option } +struct RawEdge { + source: Option, + target: Option, + label: Option, +} fn load_graph() -> Option { for path in &[ @@ -100,10 +129,14 @@ fn load_graph() -> Option { "../aiwar-neo4j-harvest/data/aiwar_graph.json".into(), "/home/user/aiwar-neo4j-harvest/data/aiwar_graph.json".into(), ] { - if path.is_empty() { continue; } + if path.is_empty() { + continue; + } if let Ok(content) = std::fs::read_to_string(path) { let cleaned = content.replace("NaN", "null"); - if let Ok(g) = serde_json::from_str::(&cleaned) { return Some(g); } + if let Ok(g) = serde_json::from_str::(&cleaned) { + return Some(g); + } } } None @@ -111,11 +144,19 @@ fn load_graph() -> Option { fn build_node_map(g: &RawGraph) -> HashMap { let mut m = HashMap::new(); - for (nodes, typ) in [(&g.systems, "System"), (&g.stakeholders, "Stakeholder"), - (&g.people, "Person"), (&g.civic, "CivicSystem"), (&g.historical, "Historical")] { + for (nodes, typ) in [ + (&g.systems, "System"), + (&g.stakeholders, "Stakeholder"), + (&g.people, "Person"), + (&g.civic, "CivicSystem"), + (&g.historical, "Historical"), + ] { for n in nodes { if let Some(id) = &n.id { - m.insert(id.clone(), (n.name.clone().unwrap_or_default(), typ.to_string())); + m.insert( + id.clone(), + (n.name.clone().unwrap_or_default(), typ.to_string()), + ); } } } @@ -134,10 +175,13 @@ fn build_truth_edges(g: &RawGraph) -> Vec { for e in raw { if let (Some(s), Some(t)) = (&e.source, &e.target) { edges.push(TruthEdge { - source: s.clone(), target: t.clone(), + source: s.clone(), + target: t.clone(), rel_type: e.label.clone().unwrap_or_else(|| rel.to_string()), truth: TruthValue::new(0.85, conf), - inferred: false, via: vec![], inference_type: None, + inferred: false, + via: vec![], + inference_type: None, }); } } @@ -210,52 +254,97 @@ pub fn analyze(bucket: AnalysisBucket) -> AnalysisResult { let all_edges = build_truth_edges(&graph); let node_map = build_node_map(&graph); - steps.push(ThinkingStep { step: 1, action: "OBSERVE".into(), + steps.push(ThinkingStep { + step: 1, + action: "OBSERVE".into(), detail: format!("Loaded {} nodes, {} edges", node_map.len(), all_edges.len()), - edges_before: 0, edges_after: all_edges.len(), new_inferences: 0 }); + edges_before: 0, + edges_after: all_edges.len(), + new_inferences: 0, + }); // Filter by bucket relevance let kw = bucket.node_keywords(); - let filtered: Vec = all_edges.iter().filter(|e| { - kw.iter().any(|k| e.source.contains(k) || e.target.contains(k)) - }).cloned().collect(); - - steps.push(ThinkingStep { step: 2, action: "FILTER".into(), - detail: format!("Filtered to {} edges for {} (keywords: {})", filtered.len(), bucket.label(), kw.join(", ")), - edges_before: all_edges.len(), edges_after: filtered.len(), new_inferences: 0 }); + let filtered: Vec = all_edges + .iter() + .filter(|e| { + kw.iter() + .any(|k| e.source.contains(k) || e.target.contains(k)) + }) + .cloned() + .collect(); + + steps.push(ThinkingStep { + step: 2, + action: "FILTER".into(), + detail: format!( + "Filtered to {} edges for {} (keywords: {})", + filtered.len(), + bucket.label(), + kw.join(", ") + ), + edges_before: all_edges.len(), + edges_after: filtered.len(), + new_inferences: 0, + }); // NARS inference let inferred = infer_edges(&filtered, 0.15, 3); let n_inf = inferred.len(); - steps.push(ThinkingStep { step: 3, action: "INFER".into(), + steps.push(ThinkingStep { + step: 3, + action: "INFER".into(), detail: format!("NARS deduction: {} new causal links discovered", n_inf), - edges_before: filtered.len(), edges_after: filtered.len() + n_inf, new_inferences: n_inf }); + edges_before: filtered.len(), + edges_after: filtered.len() + n_inf, + new_inferences: n_inf, + }); let mut relevant = filtered; relevant.extend(inferred); // Build chains let chains = build_chains(&relevant, &node_map); - steps.push(ThinkingStep { step: 4, action: "CHAIN".into(), + steps.push(ThinkingStep { + step: 4, + action: "CHAIN".into(), detail: format!("{} causality chains built", chains.len()), - edges_before: relevant.len(), edges_after: relevant.len(), new_inferences: 0 }); + edges_before: relevant.len(), + edges_after: relevant.len(), + new_inferences: 0, + }); // Project let projections = project(&chains, &relevant, bucket); - steps.push(ThinkingStep { step: 5, action: "PROJECT".into(), + steps.push(ThinkingStep { + step: 5, + action: "PROJECT".into(), detail: format!("{} projections generated", projections.len()), - edges_before: relevant.len(), edges_after: relevant.len(), new_inferences: 0 }); + edges_before: relevant.len(), + edges_after: relevant.len(), + new_inferences: 0, + }); let obs = relevant.iter().filter(|e| !e.inferred).count(); let inf = relevant.iter().filter(|e| e.inferred).count(); - let avg_c = if relevant.is_empty() { 0.0 } else { relevant.iter().map(|e| e.truth.confidence).sum::() / relevant.len() as f64 }; + let avg_c = if relevant.is_empty() { + 0.0 + } else { + relevant.iter().map(|e| e.truth.confidence).sum::() / relevant.len() as f64 + }; AnalysisResult { - bucket, label: bucket.label().into(), description: bucket.description().into(), - thinking_steps: steps, causality_chains: chains, projections, + bucket, + label: bucket.label().into(), + description: bucket.description().into(), + thinking_steps: steps, + causality_chains: chains, + projections, summary: AnalysisSummary { - total_nodes: node_map.len(), total_edges_observed: obs, total_edges_inferred: inf, + total_nodes: node_map.len(), + total_edges_observed: obs, + total_edges_inferred: inf, avg_confidence: avg_c, key_findings: findings(&relevant, &node_map, bucket), blind_spots: blind_spots(&relevant, bucket), @@ -270,20 +359,52 @@ pub fn full_analysis() -> Vec { fn empty_result(bucket: AnalysisBucket, start: std::time::Instant, msg: &str) -> AnalysisResult { AnalysisResult { - bucket, label: bucket.label().into(), description: bucket.description().into(), - thinking_steps: vec![ThinkingStep { step: 1, action: "ERROR".into(), detail: msg.into(), edges_before: 0, edges_after: 0, new_inferences: 0 }], - causality_chains: vec![], projections: vec![], - summary: AnalysisSummary { total_nodes: 0, total_edges_observed: 0, total_edges_inferred: 0, avg_confidence: 0.0, key_findings: vec![msg.into()], blind_spots: vec![] }, + bucket, + label: bucket.label().into(), + description: bucket.description().into(), + thinking_steps: vec![ThinkingStep { + step: 1, + action: "ERROR".into(), + detail: msg.into(), + edges_before: 0, + edges_after: 0, + new_inferences: 0, + }], + causality_chains: vec![], + projections: vec![], + summary: AnalysisSummary { + total_nodes: 0, + total_edges_observed: 0, + total_edges_inferred: 0, + avg_confidence: 0.0, + key_findings: vec![msg.into()], + blind_spots: vec![], + }, elapsed_us: start.elapsed().as_micros() as u64, } } -fn build_chains(edges: &[TruthEdge], node_map: &HashMap) -> Vec { +fn build_chains( + edges: &[TruthEdge], + node_map: &HashMap, +) -> Vec { let mut adj: HashMap<&str, Vec<&TruthEdge>> = HashMap::new(); - for e in edges { adj.entry(&e.source).or_default().push(e); } - let targets: std::collections::HashSet<&str> = edges.iter().map(|e| e.target.as_str()).collect(); - let starters: Vec<&str> = adj.keys().filter(|k| !targets.contains(**k)).copied().collect(); - let name = |id: &str| node_map.get(id).map(|(n, _)| n.clone()).unwrap_or_else(|| id.into()); + for e in edges { + adj.entry(&e.source).or_default().push(e); + } + let targets: std::collections::HashSet<&str> = + edges.iter().map(|e| e.target.as_str()).collect(); + let starters: Vec<&str> = adj + .keys() + .filter(|k| !targets.contains(**k)) + .copied() + .collect(); + let name = |id: &str| { + node_map + .get(id) + .map(|(n, _)| n.clone()) + .unwrap_or_else(|| id.into()) + }; let mut chains = Vec::new(); let mut seen = std::collections::HashSet::new(); @@ -294,22 +415,62 @@ fn build_chains(edges: &[TruthEdge], node_map: &HashMap= 2 { let key = format!("{}→{}", start, cur); - if seen.contains(&key) { continue; } + if seen.contains(&key) { + continue; + } seen.insert(key); - let conf = chain.iter().map(|e| e.truth.confidence).fold(1.0, |a, b| a * b); + let conf = chain + .iter() + .map(|e| e.truth.confidence) + .fold(1.0, |a, b| a * b); let has_inf = chain.iter().any(|e| e.inferred); - let narrative = chain.iter().map(|e| format!("{} →[{}]→ {}{}", name(&e.source), e.rel_type, name(&e.target), if e.inferred { " ⟹" } else { "" })).collect::>().join(" | "); - chains.push(CausalityChain { name: format!("{} → {}", name(start), name(cur)), edges: chain, confidence: conf, inference_type: if has_inf { "deduction+observed" } else { "observed" }.into(), narrative }); + let narrative = chain + .iter() + .map(|e| { + format!( + "{} →[{}]→ {}{}", + name(&e.source), + e.rel_type, + name(&e.target), + if e.inferred { " ⟹" } else { "" } + ) + }) + .collect::>() + .join(" | "); + chains.push(CausalityChain { + name: format!("{} → {}", name(start), name(cur)), + edges: chain, + confidence: conf, + inference_type: if has_inf { + "deduction+observed" + } else { + "observed" + } + .into(), + narrative, + }); } } chains.sort_by_key(|c| std::cmp::Reverse(c.edges.len())); @@ -317,52 +478,104 @@ fn build_chains(edges: &[TruthEdge], node_map: &HashMap Vec { +fn project( + chains: &[CausalityChain], + edges: &[TruthEdge], + bucket: AnalysisBucket, +) -> Vec { let mut p = Vec::new(); for c in chains.iter().take(3) { if c.confidence > 0.05 { let last = c.edges.last().map(|e| e.target.as_str()).unwrap_or("?"); p.push(Projection { - label: format!("{} chain continues", c.name), confidence: c.confidence * 0.7, + label: format!("{} chain continues", c.name), + confidence: c.confidence * 0.7, basis: c.narrative.clone(), implication: format!("{} remains central to {} dynamics", last, bucket.label()), }); } } let mut deg: HashMap<&str, usize> = HashMap::new(); - for e in edges { *deg.entry(&e.source).or_default() += 1; *deg.entry(&e.target).or_default() += 1; } + for e in edges { + *deg.entry(&e.source).or_default() += 1; + *deg.entry(&e.target).or_default() += 1; + } if let Some((node, d)) = deg.iter().max_by_key(|(_, d)| *d) { - p.push(Projection { label: format!("Hub: {}", node), confidence: 0.75, basis: format!("{} connections", d), implication: format!("Disruption to {} cascades through the network", node) }); + p.push(Projection { + label: format!("Hub: {}", node), + confidence: 0.75, + basis: format!("{} connections", d), + implication: format!("Disruption to {} cascades through the network", node), + }); } let inf = edges.iter().filter(|e| e.inferred).count(); if inf > 0 { - p.push(Projection { label: "Hidden structure".into(), confidence: 0.5, basis: format!("{} NARS-inferred connections", inf), implication: "Causal links exist that aren't directly documented — discovered through reasoning".into() }); + p.push(Projection { + label: "Hidden structure".into(), + confidence: 0.5, + basis: format!("{} NARS-inferred connections", inf), + implication: + "Causal links exist that aren't directly documented — discovered through reasoning" + .into(), + }); } p } -fn findings(edges: &[TruthEdge], nm: &HashMap, bucket: AnalysisBucket) -> Vec { - let name = |id: &str| nm.get(id).map(|(n, _)| n.clone()).unwrap_or_else(|| id.into()); +fn findings( + edges: &[TruthEdge], + nm: &HashMap, + bucket: AnalysisBucket, +) -> Vec { + let name = |id: &str| { + nm.get(id) + .map(|(n, _)| n.clone()) + .unwrap_or_else(|| id.into()) + }; let mut freq: HashMap<&str, usize> = HashMap::new(); - for e in edges { *freq.entry(&e.source).or_default() += 1; *freq.entry(&e.target).or_default() += 1; } + for e in edges { + *freq.entry(&e.source).or_default() += 1; + *freq.entry(&e.target).or_default() += 1; + } let mut sorted: Vec<_> = freq.iter().collect(); sorted.sort_by(|a, b| b.1.cmp(a.1)); let mut f = Vec::new(); - for (n, c) in sorted.iter().take(3) { f.push(format!("{} ({} connections) — central to {}", name(n), c, bucket.label())); } + for (n, c) in sorted.iter().take(3) { + f.push(format!( + "{} ({} connections) — central to {}", + name(n), + c, + bucket.label() + )); + } let inf = edges.iter().filter(|e| e.inferred).count(); - if inf > 0 { f.push(format!("NARS discovered {} hidden causal links", inf)); } - f.push(format!("{} total edges analyzed, {:.0}% avg confidence", edges.len(), edges.iter().map(|e| e.truth.confidence).sum::() / edges.len().max(1) as f64 * 100.0)); + if inf > 0 { + f.push(format!("NARS discovered {} hidden causal links", inf)); + } + f.push(format!( + "{} total edges analyzed, {:.0}% avg confidence", + edges.len(), + edges.iter().map(|e| e.truth.confidence).sum::() / edges.len().max(1) as f64 * 100.0 + )); f } fn blind_spots(edges: &[TruthEdge], bucket: AnalysisBucket) -> Vec { let mut s = Vec::new(); let low = edges.iter().filter(|e| e.truth.confidence < 0.3).count(); - if low > 0 { s.push(format!("{} low-confidence edges need verification", low)); } + if low > 0 { + s.push(format!("{} low-confidence edges need verification", low)); + } match bucket { - AnalysisBucket::EconomicReview => s.push("Chinese/Russian defense spending underrepresented".into()), - AnalysisBucket::KillChainAnalysis => s.push("Civilian casualty attribution not in graph".into()), - AnalysisBucket::SurveillanceEcosystem => s.push("Hikvision/SenseTime export data sparse".into()), + AnalysisBucket::EconomicReview => { + s.push("Chinese/Russian defense spending underrepresented".into()) + } + AnalysisBucket::KillChainAnalysis => { + s.push("Civilian casualty attribution not in graph".into()) + } + AnalysisBucket::SurveillanceEcosystem => { + s.push("Hikvision/SenseTime export data sparse".into()) + } _ => {} } s @@ -374,11 +587,16 @@ mod tests { #[test] fn all_buckets_have_keywords() { - for b in AnalysisBucket::all() { assert!(!b.node_keywords().is_empty()); } + for b in AnalysisBucket::all() { + assert!(!b.node_keywords().is_empty()); + } } #[test] fn labels_nonempty() { - for b in AnalysisBucket::all() { assert!(!b.label().is_empty()); assert!(!b.description().is_empty()); } + for b in AnalysisBucket::all() { + assert!(!b.label().is_empty()); + assert!(!b.description().is_empty()); + } } } diff --git a/crates/stubs/notebook-query/src/diagnostics.rs b/crates/stubs/notebook-query/src/diagnostics.rs index 724f22f75..6967310c8 100644 --- a/crates/stubs/notebook-query/src/diagnostics.rs +++ b/crates/stubs/notebook-query/src/diagnostics.rs @@ -71,7 +71,10 @@ pub fn run_strategy_checks() -> StrategyHealthMatrix { let test_queries = [ ("MATCH (n:System) RETURN n", "cypher"), ("MATCH (n)-[r]->(m) RETURN n, r, m", "cypher_join"), - ("MATCH (n:System) WHERE n.year > 2020 RETURN n.name ORDER BY n.year", "cypher_filter"), + ( + "MATCH (n:System) WHERE n.year > 2020 RETURN n.name ORDER BY n.year", + "cypher_filter", + ), ("MATCH path = (a)-[*1..3]->(b) RETURN path", "cypher_vlp"), ]; @@ -89,7 +92,10 @@ pub fn run_strategy_checks() -> StrategyHealthMatrix { // Demo analyses — run same query through all 12 thinking styles let demo_analyses = run_demo_analyses(&planner); - let operational = strategies.iter().filter(|s| s.status == ProbeStatus::Ready).count(); + let operational = strategies + .iter() + .filter(|s| s.status == ProbeStatus::Ready) + .count(); let total = strategies.len(); let scan_duration_us = start.elapsed().as_micros() as u64; @@ -113,9 +119,8 @@ fn probe_strategy( let start = std::time::Instant::now(); // Try the first Cypher query — most strategies should handle this - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - planner.plan(queries[0].0) - })); + let result = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| planner.plan(queries[0].0))); let (status, error, strategies_used) = match result { Ok(Ok(plan_result)) => { @@ -126,12 +131,17 @@ fn probe_strategy( (ProbeStatus::Ready, None, strats) } else { // Strategy exists but wasn't selected for this query - (ProbeStatus::Partial, Some("Not selected for test query".into()), strats) + ( + ProbeStatus::Partial, + Some("Not selected for test query".into()), + strats, + ) } } Ok(Err(e)) => (ProbeStatus::Error, Some(format!("{:?}", e)), vec![]), Err(panic) => { - let msg = panic.downcast_ref::() + let msg = panic + .downcast_ref::() .cloned() .or_else(|| panic.downcast_ref::<&str>().map(|s| s.to_string())) .unwrap_or_else(|| "unknown panic".to_string()); @@ -157,46 +167,58 @@ fn probe_strategy( fn run_pipeline_checks(planner: &Planner) -> Vec { let pipelines = [ ("Parse→Plan", "MATCH (n:System) RETURN n"), - ("Parse→Plan→Scan", "MATCH (n:System) WHERE n.year > 2020 RETURN n"), + ( + "Parse→Plan→Scan", + "MATCH (n:System) WHERE n.year > 2020 RETURN n", + ), ("Parse→Join→Scan", "MATCH (a)-[r]->(b) RETURN a, r, b"), - ("Parse→VLP→Scan", "MATCH path = (a)-[*1..3]->(b) RETURN path"), - ("Full pipeline", "MATCH (s:System)-[:DEVELOPED_BY]->(st:Stakeholder) WHERE s.year > 2020 RETURN s.name, st.name ORDER BY s.year DESC LIMIT 10"), + ( + "Parse→VLP→Scan", + "MATCH path = (a)-[*1..3]->(b) RETURN path", + ), + ( + "Full pipeline", + "MATCH (s:System)-[:DEVELOPED_BY]->(st:Stakeholder) WHERE s.year > 2020 RETURN s.name, st.name ORDER BY s.year DESC LIMIT 10", + ), ]; - pipelines.iter().map(|(name, query)| { - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - planner.plan(query) - })); - - match result { - Ok(Ok(plan)) => PipelineProbeResult { - name: name.to_string(), - stages: plan.strategies_used.clone(), - passed: true, - broke_at: None, - error: None, - }, - Ok(Err(e)) => PipelineProbeResult { - name: name.to_string(), - stages: vec![], - passed: false, - broke_at: Some(format!("{:?}", e)), - error: Some(format!("{:?}", e)), - }, - Err(panic) => { - let msg = panic.downcast_ref::() - .cloned() - .unwrap_or_else(|| "panic".to_string()); - PipelineProbeResult { + pipelines + .iter() + .map(|(name, query)| { + let result = + std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| planner.plan(query))); + + match result { + Ok(Ok(plan)) => PipelineProbeResult { + name: name.to_string(), + stages: plan.strategies_used.clone(), + passed: true, + broke_at: None, + error: None, + }, + Ok(Err(e)) => PipelineProbeResult { name: name.to_string(), stages: vec![], passed: false, - broke_at: Some(msg.clone()), - error: Some(msg), + broke_at: Some(format!("{:?}", e)), + error: Some(format!("{:?}", e)), + }, + Err(panic) => { + let msg = panic + .downcast_ref::() + .cloned() + .unwrap_or_else(|| "panic".to_string()); + PipelineProbeResult { + name: name.to_string(), + stages: vec![], + passed: false, + broke_at: Some(msg.clone()), + error: Some(msg), + } } } - } - }).collect() + }) + .collect() } // ── Demo Analyses: 12 Thinking Styles × Same Query ────────────────────────── @@ -257,66 +279,72 @@ fn run_demo_analyses(planner: &Planner) -> Vec { StyleFamily::Metacognitive, ]; - demo_queries.iter().map(|query| { - let style_results: Vec = all_styles.iter().map(|style| { - let start = std::time::Instant::now(); - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - planner.plan_with_style(query, *style) - })); - - let modulation = style.default_modulation(); - let mod_snapshot = ModulationSnapshot { - resonance_threshold: modulation.resonance_threshold, - fan_out: modulation.fan_out as u32, - depth_bias: modulation.depth_bias, - breadth_bias: modulation.breadth_bias, - noise_tolerance: modulation.noise_tolerance, - speed_bias: modulation.speed_bias, - exploration: modulation.exploration, - }; - - match result { - Ok(Ok(plan)) => StyleAnalysisResult { - style: format!("{:?}", style), - cluster: format!("{:?}", style.cluster()), - strategies_used: plan.strategies_used.clone(), - strategy_count: plan.strategies_used.len(), - free_will_modifier: plan.free_will_modifier, - latency_us: start.elapsed().as_micros() as u64, - status: ProbeStatus::Ready, - error: None, - modulation: mod_snapshot, - }, - Ok(Err(e)) => StyleAnalysisResult { - style: format!("{:?}", style), - cluster: format!("{:?}", style.cluster()), - strategies_used: vec![], - strategy_count: 0, - free_will_modifier: 0.0, - latency_us: start.elapsed().as_micros() as u64, - status: ProbeStatus::Error, - error: Some(format!("{:?}", e)), - modulation: mod_snapshot, - }, - Err(_) => StyleAnalysisResult { - style: format!("{:?}", style), - cluster: format!("{:?}", style.cluster()), - strategies_used: vec![], - strategy_count: 0, - free_will_modifier: 0.0, - latency_us: start.elapsed().as_micros() as u64, - status: ProbeStatus::Dead, - error: Some("panic during planning".to_string()), - modulation: mod_snapshot, - }, + demo_queries + .iter() + .map(|query| { + let style_results: Vec = all_styles + .iter() + .map(|style| { + let start = std::time::Instant::now(); + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + planner.plan_with_style(query, *style) + })); + + let modulation = style.default_modulation(); + let mod_snapshot = ModulationSnapshot { + resonance_threshold: modulation.resonance_threshold, + fan_out: modulation.fan_out as u32, + depth_bias: modulation.depth_bias, + breadth_bias: modulation.breadth_bias, + noise_tolerance: modulation.noise_tolerance, + speed_bias: modulation.speed_bias, + exploration: modulation.exploration, + }; + + match result { + Ok(Ok(plan)) => StyleAnalysisResult { + style: format!("{:?}", style), + cluster: format!("{:?}", style.cluster()), + strategies_used: plan.strategies_used.clone(), + strategy_count: plan.strategies_used.len(), + free_will_modifier: plan.free_will_modifier, + latency_us: start.elapsed().as_micros() as u64, + status: ProbeStatus::Ready, + error: None, + modulation: mod_snapshot, + }, + Ok(Err(e)) => StyleAnalysisResult { + style: format!("{:?}", style), + cluster: format!("{:?}", style.cluster()), + strategies_used: vec![], + strategy_count: 0, + free_will_modifier: 0.0, + latency_us: start.elapsed().as_micros() as u64, + status: ProbeStatus::Error, + error: Some(format!("{:?}", e)), + modulation: mod_snapshot, + }, + Err(_) => StyleAnalysisResult { + style: format!("{:?}", style), + cluster: format!("{:?}", style.cluster()), + strategies_used: vec![], + strategy_count: 0, + free_will_modifier: 0.0, + latency_us: start.elapsed().as_micros() as u64, + status: ProbeStatus::Dead, + error: Some("panic during planning".to_string()), + modulation: mod_snapshot, + }, + } + }) + .collect(); + + DemoAnalysis { + query: query.to_string(), + style_results, } - }).collect(); - - DemoAnalysis { - query: query.to_string(), - style_results, - } - }).collect() + }) + .collect() } // ── Fallback when planner feature is off ───────────────────────────────────── @@ -329,7 +357,9 @@ pub fn run_strategy_checks() -> StrategyHealthMatrix { index: 0, status: ProbeStatus::Stub, latency_us: 0, - error: Some("Compile with --features planner to enable live strategy checks".to_string()), + error: Some( + "Compile with --features planner to enable live strategy checks".to_string(), + ), strategies_used: vec![], }], pipelines: vec![], diff --git a/crates/stubs/notebook-query/src/gremlin.rs b/crates/stubs/notebook-query/src/gremlin.rs index be27d8b97..4e02420f3 100644 --- a/crates/stubs/notebook-query/src/gremlin.rs +++ b/crates/stubs/notebook-query/src/gremlin.rs @@ -71,11 +71,7 @@ fn parse_steps(src: &str) -> Option> { } steps.push(Step { name, args }); } - if steps.is_empty() { - None - } else { - Some(steps) - } + if steps.is_empty() { None } else { Some(steps) } } /// Transpile a Gremlin traversal to Cypher, or `None` if unsupported. @@ -154,10 +150,10 @@ pub fn gremlin_to_cypher(gremlin: &str) -> Option { "out" | "in" | "both" => { close_node(&mut pattern, &mut node_open, &mut cur_has_label); let rel = step - .args - .first() - .map(|r| format!(":{}", sanitize_label(r))) - .unwrap_or_else(|| ":Edge".to_string()); + .args + .first() + .map(|r| format!(":{}", sanitize_label(r))) + .unwrap_or_else(|| ":Edge".to_string()); let nv = fresh(&mut var_idx); let arrow = match step.name.as_str() { "out" => format!("-[{rel}]->("), @@ -174,10 +170,10 @@ pub fn gremlin_to_cypher(gremlin: &str) -> Option { close_node(&mut pattern, &mut node_open, &mut cur_has_label); let ev = format!("e{}", edge_vars.len()); let rel = step - .args - .first() - .map(|r| format!(":{}", sanitize_label(r))) - .unwrap_or_else(|| ":Edge".to_string()); + .args + .first() + .map(|r| format!(":{}", sanitize_label(r))) + .unwrap_or_else(|| ":Edge".to_string()); let dir = match step.name.as_str() { "outE" => format!("-[{ev}{rel}]->("), "inE" => format!("<-[{ev}{rel}]-("), @@ -247,11 +243,15 @@ fn escape(s: &str) -> String { /// Labels and rel types are identifiers in Cypher — keep alnum/underscore only. fn sanitize_label(s: &str) -> String { - s.chars().filter(|c| c.is_alphanumeric() || *c == '_').collect() + s.chars() + .filter(|c| c.is_alphanumeric() || *c == '_') + .collect() } fn sanitize_ident(s: &str) -> String { - s.chars().filter(|c| c.is_alphanumeric() || *c == '_').collect() + s.chars() + .filter(|c| c.is_alphanumeric() || *c == '_') + .collect() } #[cfg(test)] @@ -278,7 +278,8 @@ mod tests { #[test] fn out_with_rel_type_and_limit() { - let c = gremlin_to_cypher("g.V().hasLabel('System').out('DEVELOPED_BY').limit(10)").unwrap(); + let c = + gremlin_to_cypher("g.V().hasLabel('System').out('DEVELOPED_BY').limit(10)").unwrap(); assert_eq!( c, "MATCH (n0:System)-[:DEVELOPED_BY]->(n1:Entity) RETURN n1.id, n1.name LIMIT 10" diff --git a/crates/stubs/notebook-query/src/hydration.rs b/crates/stubs/notebook-query/src/hydration.rs index 40f8c2f9e..5c1bcd814 100644 --- a/crates/stubs/notebook-query/src/hydration.rs +++ b/crates/stubs/notebook-query/src/hydration.rs @@ -71,10 +71,15 @@ pub fn hhtl_heel_search( .iter() .enumerate() .map(|(i, &(pos, dist))| { - let stage = if i < heel_count / 4 { HhtlStage::Heel } - else if i < heel_count / 2 { HhtlStage::Hip } - else if i < heel_count * 3 / 4 { HhtlStage::Twig } - else { HhtlStage::Leaf }; + let stage = if i < heel_count / 4 { + HhtlStage::Heel + } else if i < heel_count / 2 { + HhtlStage::Hip + } else if i < heel_count * 3 / 4 { + HhtlStage::Twig + } else { + HhtlStage::Leaf + }; HhtlHit { node_id: format!("node-{pos}"), distance: dist, @@ -123,10 +128,15 @@ pub fn hhtl_full_cascade( .iter() .enumerate() .map(|(i, hit)| { - let stage = if i < leaf_count / 4 { HhtlStage::Heel } - else if i < leaf_count / 2 { HhtlStage::Hip } - else if i < leaf_count * 3 / 4 { HhtlStage::Twig } - else { HhtlStage::Leaf }; + let stage = if i < leaf_count / 4 { + HhtlStage::Heel + } else if i < leaf_count / 2 { + HhtlStage::Hip + } else if i < leaf_count * 3 / 4 { + HhtlStage::Twig + } else { + HhtlStage::Leaf + }; HhtlHit { node_id: format!("node-{}", hit.position), distance: hit.best_distance, @@ -240,11 +250,7 @@ pub fn fingerprint_distance(a: &Fingerprint, b: &Fingerprint) -> u32 { } /// Find the top-K most similar labels by fingerprint distance. -pub fn find_similar( - query_label: &str, - all_labels: &[&str], - top_k: usize, -) -> Vec<(String, u32)> { +pub fn find_similar(query_label: &str, all_labels: &[&str], top_k: usize) -> Vec<(String, u32)> { let query_fp = fingerprint::label_fp(query_label); let mut results: Vec<(String, u32)> = all_labels .iter() diff --git a/crates/stubs/notebook-query/src/lib.rs b/crates/stubs/notebook-query/src/lib.rs index 04c16ace2..ba057c86a 100644 --- a/crates/stubs/notebook-query/src/lib.rs +++ b/crates/stubs/notebook-query/src/lib.rs @@ -126,9 +126,7 @@ pub fn execute(source: &str, language: QueryLanguage) -> Result execute_cypher(source), - QueryLanguage::Gremlin | QueryLanguage::Sparql => { - execute_graph_query(source, language) - } + QueryLanguage::Gremlin | QueryLanguage::Sparql => execute_graph_query(source, language), QueryLanguage::R => Ok(QueryResult { language, raw_output: format!("R output for: {}", source), @@ -645,8 +643,8 @@ fn load_aiwar_datasets() -> Result<&'static (HashMap, Graph if path.is_empty() { return Err("Cannot find aiwar_graph.json — set AIWAR_DATA_PATH".to_string()); } - let content = std::fs::read_to_string(&path) - .map_err(|e| format!("Failed to read {path}: {e}"))?; + let content = + std::fs::read_to_string(&path).map_err(|e| format!("Failed to read {path}: {e}"))?; let data: AiWarGraphJson = serde_json::from_str(&content).map_err(|e| format!("JSON parse error: {e}"))?; @@ -1255,7 +1253,8 @@ fn aiwar_graph_json() -> Result { let id_idx = schema.index_of("id").ok(); let name_idx = schema.index_of("name").ok(); // Pre-collect field names once per table — avoids clone per row×column - let field_names: Vec<&str> = schema.fields().iter().map(|f| f.name().as_str()).collect(); + let field_names: Vec<&str> = + schema.fields().iter().map(|f| f.name().as_str()).collect(); for row in 0..batch.num_rows() { let id_val = id_idx @@ -1379,7 +1378,9 @@ fn get_string_value(batch: &RecordBatch, col: usize, row: usize) -> Option { // codebook column: resolve the u16 index back to its string value. - let dict = col_data.as_any().downcast_ref::>()?; + let dict = col_data + .as_any() + .downcast_ref::>()?; let values = dict.values().as_any().downcast_ref::()?; let key = dict.keys().value(row) as usize; Some(values.value(key).to_string()) @@ -1414,7 +1415,9 @@ fn get_json_value(batch: &RecordBatch, col: usize, row: usize) -> Option { // codebook column: resolve the u16 index back to its string value. - let dict = col_data.as_any().downcast_ref::>()?; + let dict = col_data + .as_any() + .downcast_ref::>()?; let values = dict.values().as_any().downcast_ref::()?; let key = dict.keys().value(row) as usize; Some(serde_json::Value::String(values.value(key).to_string())) @@ -1462,12 +1465,28 @@ pub fn extract_graph_truth_edges() -> Result, String> if let (Some(si), Some(ti)) = (src_idx, tgt_idx) { for row in 0..batch.num_rows() { - let src = batch.column(si).as_any() + let src = batch + .column(si) + .as_any() .downcast_ref::() - .and_then(|a| if a.is_null(row) { None } else { Some(a.value(row).to_string()) }); - let tgt = batch.column(ti).as_any() + .and_then(|a| { + if a.is_null(row) { + None + } else { + Some(a.value(row).to_string()) + } + }); + let tgt = batch + .column(ti) + .as_any() .downcast_ref::() - .and_then(|a| if a.is_null(row) { None } else { Some(a.value(row).to_string()) }); + .and_then(|a| { + if a.is_null(row) { + None + } else { + Some(a.value(row).to_string()) + } + }); if let (Some(source), Some(target)) = (src, tgt) { // Check for weight column @@ -1510,29 +1529,35 @@ pub fn extract_graph_truth_edges() -> Result, String> /// rendering instructions. pub fn get_graph_truth_summary(min_confidence: f64) -> Result { let edges = extract_graph_truth_edges()?; - let filtered: Vec<&reasoning::TruthEdge> = edges.iter() + let filtered: Vec<&reasoning::TruthEdge> = edges + .iter() .filter(|e| e.truth.confidence >= min_confidence) .collect(); // Summary by relationship type - let mut by_type: std::collections::HashMap<&str, Vec<(f64, f64)>> = std::collections::HashMap::new(); + let mut by_type: std::collections::HashMap<&str, Vec<(f64, f64)>> = + std::collections::HashMap::new(); for e in &filtered { - by_type.entry(&e.rel_type) + by_type + .entry(&e.rel_type) .or_default() .push((e.truth.frequency, e.truth.confidence)); } - let type_summaries: Vec = by_type.iter().map(|(rel_type, values)| { - let avg_freq = values.iter().map(|(f, _)| f).sum::() / values.len() as f64; - let avg_conf = values.iter().map(|(_, c)| c).sum::() / values.len() as f64; - serde_json::json!({ - "rel_type": rel_type, - "count": values.len(), - "avg_frequency": (avg_freq * 1000.0).round() / 1000.0, - "avg_confidence": (avg_conf * 1000.0).round() / 1000.0, - "avg_expectation": ((avg_conf * (avg_freq - 0.5) + 0.5) * 1000.0).round() / 1000.0, + let type_summaries: Vec = by_type + .iter() + .map(|(rel_type, values)| { + let avg_freq = values.iter().map(|(f, _)| f).sum::() / values.len() as f64; + let avg_conf = values.iter().map(|(_, c)| c).sum::() / values.len() as f64; + serde_json::json!({ + "rel_type": rel_type, + "count": values.len(), + "avg_frequency": (avg_freq * 1000.0).round() / 1000.0, + "avg_confidence": (avg_conf * 1000.0).round() / 1000.0, + "avg_expectation": ((avg_conf * (avg_freq - 0.5) + 0.5) * 1000.0).round() / 1000.0, + }) }) - }).collect(); + .collect(); Ok(serde_json::json!({ "total_edges": edges.len(), diff --git a/crates/stubs/notebook-query/src/mri.rs b/crates/stubs/notebook-query/src/mri.rs index 38367a29d..973ca0cdd 100644 --- a/crates/stubs/notebook-query/src/mri.rs +++ b/crates/stubs/notebook-query/src/mri.rs @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use super::osint_audit::{osint_registry, OsintCounterSnapshot}; +use super::osint_audit::{OsintCounterSnapshot, osint_registry}; use super::reasoning::{InferenceType, TruthEdge, TruthValue}; // ============================================================================ @@ -306,7 +306,10 @@ pub fn run_brain_mri( .filter(|p| p.state == PlasticityState::Hot) .count(); if hot_count > 0 { - findings.push(format!("{} entities are actively learning (hot plasticity)", hot_count)); + findings.push(format!( + "{} entities are actively learning (hot plasticity)", + hot_count + )); } let frozen_count = plasticity_map .iter() @@ -367,7 +370,12 @@ fn build_brain_regions(stages: &[(String, OsintCounterSnapshot)]) -> Vec Vec { let step = InferenceStep { rule: rule.to_string(), - premise_a: format!("{} → {}", edge.source, edge.via.first().unwrap_or(&edge.target)), + premise_a: format!( + "{} → {}", + edge.source, + edge.via.first().unwrap_or(&edge.target) + ), premise_b: format!( "{} → {}", edge.via.last().unwrap_or(&edge.source), @@ -545,10 +557,11 @@ mod tests { assert_eq!(mri.dominant_mode, "Analytical"); assert!(!mri.findings.is_empty()); // Should detect US_DoD as conflicted (1 contradiction) - assert!(mri - .plasticity_map - .iter() - .any(|p| p.entity == "US_DoD" && p.state == PlasticityState::Conflicted)); + assert!( + mri.plasticity_map + .iter() + .any(|p| p.entity == "US_DoD" && p.state == PlasticityState::Conflicted) + ); // Should have reasoning chains (1 inferred edge) assert!(!mri.reasoning_chains.is_empty()); } @@ -566,49 +579,91 @@ mod tests { #[test] fn test_plasticity_states() { let mut stats = HashMap::new(); - stats.insert("hot_entity".into(), EntityStats { - triplet_count: 10, - avg_confidence: 0.5, - revisions: 20, - contradictions: 0, - }); - stats.insert("frozen_entity".into(), EntityStats { - triplet_count: 5, - avg_confidence: 0.95, - revisions: 1, - contradictions: 0, - }); - stats.insert("conflicted_entity".into(), EntityStats { - triplet_count: 3, - avg_confidence: 0.6, - revisions: 2, - contradictions: 2, - }); + stats.insert( + "hot_entity".into(), + EntityStats { + triplet_count: 10, + avg_confidence: 0.5, + revisions: 20, + contradictions: 0, + }, + ); + stats.insert( + "frozen_entity".into(), + EntityStats { + triplet_count: 5, + avg_confidence: 0.95, + revisions: 1, + contradictions: 0, + }, + ); + stats.insert( + "conflicted_entity".into(), + EntityStats { + triplet_count: 3, + avg_confidence: 0.6, + revisions: 2, + contradictions: 2, + }, + ); let mri = run_brain_mri(&[], &stats, &[], ScanMode::Full); - let hot = mri.plasticity_map.iter().find(|p| p.entity == "hot_entity").unwrap(); + let hot = mri + .plasticity_map + .iter() + .find(|p| p.entity == "hot_entity") + .unwrap(); assert_eq!(hot.state, PlasticityState::Hot); - let frozen = mri.plasticity_map.iter().find(|p| p.entity == "frozen_entity").unwrap(); + let frozen = mri + .plasticity_map + .iter() + .find(|p| p.entity == "frozen_entity") + .unwrap(); assert_eq!(frozen.state, PlasticityState::Frozen); - let conflicted = mri.plasticity_map.iter().find(|p| p.entity == "conflicted_entity").unwrap(); + let conflicted = mri + .plasticity_map + .iter() + .find(|p| p.entity == "conflicted_entity") + .unwrap(); assert_eq!(conflicted.state, PlasticityState::Conflicted); } #[test] fn test_brain_regions() { let stages = vec![ - ("extraction".into(), OsintCounterSnapshot { - calls: 10, successes: 9, failures: 1, avg_latency_us: 500, triplets_produced: 45, - }), - ("deduction".into(), OsintCounterSnapshot { - calls: 5, successes: 5, failures: 0, avg_latency_us: 100, triplets_produced: 12, - }), - ("episodic_store".into(), OsintCounterSnapshot { - calls: 0, successes: 0, failures: 0, avg_latency_us: 0, triplets_produced: 0, - }), + ( + "extraction".into(), + OsintCounterSnapshot { + calls: 10, + successes: 9, + failures: 1, + avg_latency_us: 500, + triplets_produced: 45, + }, + ), + ( + "deduction".into(), + OsintCounterSnapshot { + calls: 5, + successes: 5, + failures: 0, + avg_latency_us: 100, + triplets_produced: 12, + }, + ), + ( + "episodic_store".into(), + OsintCounterSnapshot { + calls: 0, + successes: 0, + failures: 0, + avg_latency_us: 0, + triplets_produced: 0, + }, + ), ]; let regions = build_brain_regions(&stages); @@ -619,7 +674,12 @@ mod tests { let memory = regions.iter().find(|r| r.name == "memory").unwrap(); // episodic_store has 0 calls, so some sub-regions are dead - assert!(memory.sub_regions.iter().any(|s| s.status == RegionStatus::Dead)); + assert!( + memory + .sub_regions + .iter() + .any(|s| s.status == RegionStatus::Dead) + ); } #[test] diff --git a/crates/stubs/notebook-query/src/ontology.rs b/crates/stubs/notebook-query/src/ontology.rs index c4fd1624c..115487597 100644 --- a/crates/stubs/notebook-query/src/ontology.rs +++ b/crates/stubs/notebook-query/src/ontology.rs @@ -8,7 +8,7 @@ use std::sync::LazyLock; -use lance_graph_contract::ontology::{entity_type_id, EntityTypeId, Ontology}; +use lance_graph_contract::ontology::{EntityTypeId, Ontology, entity_type_id}; use lance_graph_contract::property::{LinkSpec, Schema}; /// The aiwar ontology: five node entity types. Schema **order fixes the diff --git a/crates/stubs/notebook-query/src/orchestrator.rs b/crates/stubs/notebook-query/src/orchestrator.rs index 59341a010..48c19538c 100644 --- a/crates/stubs/notebook-query/src/orchestrator.rs +++ b/crates/stubs/notebook-query/src/orchestrator.rs @@ -97,7 +97,7 @@ impl DkPosition { /// Exploration rate modifier: how much to explore vs exploit. pub fn exploration_modifier(&self) -> f32 { match self { - Self::MountStupid => 0.0, // Don't explore, you're overconfident + Self::MountStupid => 0.0, // Don't explore, you're overconfident Self::ValleyOfDespair => 2.0, // Explore heavily, you're learning Self::SlopeOfEnlightenment => 1.0, // Normal Self::PlateauOfMastery => 0.5, // Exploit more, you've earned it @@ -150,9 +150,9 @@ impl FlowState { pub fn patience_modifier(&self) -> f32 { match self { Self::Flow => 1.0, - Self::Boredom => 1.5, // More patient, try more things - Self::Anxiety => 0.5, // Less patient, fallback sooner - Self::Apathy => 0.25, // Very impatient + Self::Boredom => 1.5, // More patient, try more things + Self::Anxiety => 0.5, // Less patient, fallback sooner + Self::Apathy => 0.25, // Very impatient } } } @@ -230,17 +230,14 @@ impl MulAssessment { // Compass override let compass = if dk_position == DkPosition::MountStupid { CompassDecision::ForceSandbox - } else if dk_position == DkPosition::ValleyOfDespair - && demonstrated_competence < 0.2 - { + } else if dk_position == DkPosition::ValleyOfDespair && demonstrated_competence < 0.2 { CompassDecision::ForceExplore } else { CompassDecision::None }; - let free_will = dk_position.humility_factor() - * trust.trust_factor() - * flow.patience_modifier(); + let free_will = + dk_position.humility_factor() * trust.trust_factor() * flow.patience_modifier(); let exploration_rate = BASE_EXPLORATION_RATE * dk_position.exploration_modifier() * trust.trust_factor(); @@ -263,10 +260,10 @@ impl MulAssessment { /// topology confidence as felt competence. pub fn from_efficiency(efficiency: f32, topology_confidence: f32) -> Self { Self::assess( - topology_confidence, // felt = how confident the topology is - efficiency, // demonstrated = actual rolling efficiency - 0.7, // default source reliability - 0.8, // default environment stability + topology_confidence, // felt = how confident the topology is + efficiency, // demonstrated = actual rolling efficiency + 0.7, // default source reliability + 0.8, // default environment stability (efficiency - 0.3).abs().clamp(0.0, 1.0), // challenge ~ distance from mediocrity ) } @@ -310,7 +307,13 @@ impl MulAssessment { 0.1 // No challenge, no skill → Boredom }; - Self::assess(felt, demonstrated, source_reliability, stability, challenge_skill) + Self::assess( + felt, + demonstrated, + source_reliability, + stability, + challenge_skill, + ) } } @@ -583,10 +586,7 @@ impl MetaOrchestrator { // If the orchestrator's own topology is producing consistently bad results // AND the graph is in bad shape, reset the topology too. - if self.rolling_efficiency() < 0.2 - && self.step_count > 20 - && signals.truth_entropy > 0.5 - { + if self.rolling_efficiency() < 0.2 && self.step_count > 20 && signals.truth_entropy > 0.5 { actions.push(HealingAction { action: HealingType::ResetTopology, reason: format!( @@ -748,11 +748,7 @@ impl StyleTopology { /// /// With probability `exploration_rate`, picks a random style (explore). /// Otherwise picks the highest expected quality. - pub fn select_next( - &self, - current: AgentStyle, - step: u64, - ) -> AgentStyle { + pub fn select_next(&self, current: AgentStyle, step: u64) -> AgentStyle { // Deterministic "random" from step counter for reproducibility. let pseudo_random = ((step.wrapping_mul(0x9E3779B97F4A7C15)) >> 56) as f32 / 256.0; @@ -906,13 +902,9 @@ pub enum StepReason { }, /// Exploration: picked least-observed edge for information gain. /// Exploration rate was modulated by DK position + trust texture. - TopologyExplore { - observations: u64, - }, + TopologyExplore { observations: u64 }, /// Hardcoded sequence position (fallback mode). - HardcodedSequence { - position: usize, - }, + HardcodedSequence { position: usize }, /// MUL compass/DK override — forced a specific style regardless of topology. MulOverride { dk: DkPosition, @@ -988,11 +980,20 @@ impl MetaOrchestrator { self.steps_in_current_mode += 1; // ── MUL Assessment (self-regulated from graph signals when available) ── - let avg_confidence = self.topology.edges.values() + let avg_confidence = self + .topology + .edges + .values() .filter(|e| e.observations > 0) .map(|e| e.truth.confidence) .sum::() - / self.topology.edges.values().filter(|e| e.observations > 0).count().max(1) as f64; + / self + .topology + .edges + .values() + .filter(|e| e.observations > 0) + .count() + .max(1) as f64; let mul = if let Some(ref signals) = self.sensorium { MulAssessment::from_graph_signals(signals, avg_confidence as f32) } else { @@ -1023,7 +1024,8 @@ impl MetaOrchestrator { reason: StepReason::MulOverride { dk: mul.dk_position, compass: mul.compass, - explanation: "Valley of Despair + low competence — compass forces exploration".into(), + explanation: "Valley of Despair + low competence — compass forces exploration" + .into(), }, mul, efficiency: self.rolling_efficiency(), @@ -1069,7 +1071,8 @@ impl MetaOrchestrator { // Temperature noise: deterministic from step + style index let noise = if self.temperature > 0.01 { let hash = (self.step_count.wrapping_mul(0x517cc1b727220a95) - ^ (to as u64).wrapping_mul(0x6c62272e07bb0142)) >> 48; + ^ (to as u64).wrapping_mul(0x6c62272e07bb0142)) + >> 48; let uniform = (hash as f64) / 65536.0; // [0, 1) (uniform - 0.5) * self.temperature as f64 } else { @@ -1438,12 +1441,17 @@ mod tests { #[test] fn test_graph_sensorium_healthy() { let signals = GraphSensorium::compute( - 100, 2, // 100 active, 2 contradictions - &[80, 10, 5, 3, 2], // mostly certain - 5, 10, // 5 revisions in 10 steps - 3, 50, // 3/50 entities hot - 10, 15, // 10 deductions from 15 attempts - 5, 20, // 5/20 episodic + 100, + 2, // 100 active, 2 contradictions + &[80, 10, 5, 3, 2], // mostly certain + 5, + 10, // 5 revisions in 10 steps + 3, + 50, // 3/50 entities hot + 10, + 15, // 10 deductions from 15 attempts + 5, + 20, // 5/20 episodic ); assert!(signals.contradiction_rate < 0.1); assert!(signals.truth_entropy < 0.5); // mostly certain @@ -1453,12 +1461,17 @@ mod tests { #[test] fn test_graph_sensorium_contradicted() { let signals = GraphSensorium::compute( - 100, 40, // 40% contradictions! - &[10, 10, 30, 30, 20], // spread across bands - 1, 10, // low revision - 20, 50, // 40% hot - 2, 20, // low deduction - 18, 20, // near-full episodic + 100, + 40, // 40% contradictions! + &[10, 10, 30, 30, 20], // spread across bands + 1, + 10, // low revision + 20, + 50, // 40% hot + 2, + 20, // low deduction + 18, + 20, // near-full episodic ); assert!(signals.contradiction_rate > 0.3); assert_eq!(signals.suggested_bias(), GraphBias::Resolve); @@ -1467,12 +1480,17 @@ mod tests { #[test] fn test_graph_sensorium_stagnant() { let signals = GraphSensorium::compute( - 100, 5, - &[20, 20, 20, 20, 20], // uniform = high entropy - 0, 20, // zero revisions = stagnant - 2, 100, // low plasticity - 0, 10, // zero deductions - 5, 20, + 100, + 5, + &[20, 20, 20, 20, 20], // uniform = high entropy + 0, + 20, // zero revisions = stagnant + 2, + 100, // low plasticity + 0, + 10, // zero deductions + 5, + 20, ); assert!(signals.revision_velocity < 0.05); assert!(signals.truth_entropy > 0.5); @@ -1484,9 +1502,8 @@ mod tests { let mut orch = MetaOrchestrator::new(); assert!((orch.temperature - 0.0).abs() < f32::EPSILON); - let stagnant = GraphSensorium::compute( - 100, 5, &[20, 20, 20, 20, 20], 0, 20, 2, 100, 0, 10, 5, 20, - ); + let stagnant = + GraphSensorium::compute(100, 5, &[20, 20, 20, 20, 20], 0, 20, 2, 100, 0, 10, 5, 20); orch.update_sensorium(stagnant.clone()); assert!(orch.temperature > 0.1); @@ -1500,18 +1517,16 @@ mod tests { let mut orch = MetaOrchestrator::new(); orch.temperature = 0.5; - let healthy = GraphSensorium::compute( - 100, 1, &[90, 5, 3, 1, 1], 8, 10, 1, 100, 12, 15, 5, 20, - ); + let healthy = + GraphSensorium::compute(100, 1, &[90, 5, 3, 1, 1], 8, 10, 1, 100, 12, 15, 5, 20); orch.update_sensorium(healthy); assert!(orch.temperature < 0.5); } #[test] fn test_mul_from_graph_signals() { - let signals = GraphSensorium::compute( - 100, 2, &[80, 10, 5, 3, 2], 5, 10, 3, 50, 10, 15, 5, 20, - ); + let signals = + GraphSensorium::compute(100, 2, &[80, 10, 5, 3, 2], 5, 10, 3, 50, 10, 15, 5, 20); let mul = MulAssessment::from_graph_signals(&signals, 0.7); // Healthy graph → should be Slope or Plateau. assert!(mul.dk_position != DkPosition::MountStupid); @@ -1521,23 +1536,29 @@ mod tests { #[test] fn test_auto_heal_contradictions() { let mut orch = MetaOrchestrator::new(); - let sick = GraphSensorium::compute( - 100, 30, &[10, 10, 30, 30, 20], 1, 10, 20, 50, 2, 20, 18, 20, - ); + let sick = + GraphSensorium::compute(100, 30, &[10, 10, 30, 30, 20], 1, 10, 20, 50, 2, 20, 18, 20); orch.update_sensorium(sick); let actions = orch.auto_heal(); - assert!(actions.iter().any(|a| a.action == HealingType::ResolveContradictions)); + assert!( + actions + .iter() + .any(|a| a.action == HealingType::ResolveContradictions) + ); } #[test] fn test_auto_heal_bootstrap_truth() { let mut orch = MetaOrchestrator::new(); - let unset = GraphSensorium::compute( - 100, 5, &[20, 20, 20, 20, 20], 0, 20, 2, 100, 0, 10, 5, 20, - ); + let unset = + GraphSensorium::compute(100, 5, &[20, 20, 20, 20, 20], 0, 20, 2, 100, 0, 10, 5, 20); orch.update_sensorium(unset); let actions = orch.auto_heal(); - assert!(actions.iter().any(|a| a.action == HealingType::BootstrapTruth)); + assert!( + actions + .iter() + .any(|a| a.action == HealingType::BootstrapTruth) + ); } #[test] @@ -1549,13 +1570,16 @@ mod tests { orch.record_outcome(r.style, 0.05); } // Now feed sick graph signals. - let sick = GraphSensorium::compute( - 100, 30, &[20, 20, 20, 20, 20], 0, 20, 20, 50, 0, 20, 18, 20, - ); + let sick = + GraphSensorium::compute(100, 30, &[20, 20, 20, 20, 20], 0, 20, 20, 50, 0, 20, 18, 20); orch.update_sensorium(sick); let actions = orch.auto_heal(); // Should trigger topology reset. - assert!(actions.iter().any(|a| a.action == HealingType::ResetTopology)); + assert!( + actions + .iter() + .any(|a| a.action == HealingType::ResetTopology) + ); // Should be back in fallback mode. assert_eq!(orch.mode, OrchestratorMode::HardcodedFallback); // Temperature should be warm (0.5 from reset). @@ -1564,9 +1588,8 @@ mod tests { #[test] fn test_graph_bias_exploit() { - let signals = GraphSensorium::compute( - 200, 2, &[180, 10, 5, 3, 2], 15, 20, 2, 100, 15, 20, 5, 50, - ); + let signals = + GraphSensorium::compute(200, 2, &[180, 10, 5, 3, 2], 15, 20, 2, 100, 15, 20, 5, 50); assert_eq!(signals.suggested_bias(), GraphBias::Exploit); } } diff --git a/crates/stubs/notebook-query/src/osint_audit.rs b/crates/stubs/notebook-query/src/osint_audit.rs index 4ef81d0ed..31b649812 100644 --- a/crates/stubs/notebook-query/src/osint_audit.rs +++ b/crates/stubs/notebook-query/src/osint_audit.rs @@ -7,8 +7,8 @@ //! Exposed via q2 cockpit-server at `/api/debug/osint`. use serde::{Deserialize, Serialize}; -use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::OnceLock; +use std::sync::atomic::{AtomicU64, Ordering}; // ── Global OSINT Pipeline Registry ────────────────────────────────────────── @@ -51,7 +51,8 @@ impl OsintCounter { self.calls.fetch_add(1, Ordering::Relaxed); self.successes.fetch_add(1, Ordering::Relaxed); self.total_ns.fetch_add(elapsed_ns, Ordering::Relaxed); - self.triplets_produced.fetch_add(triplets, Ordering::Relaxed); + self.triplets_produced + .fetch_add(triplets, Ordering::Relaxed); } pub fn record_failure(&self, elapsed_ns: u64) { @@ -134,7 +135,10 @@ impl OsintRegistry { ("contradiction".into(), self.contradiction.snapshot()), ("revision".into(), self.revision.snapshot()), ("episodic_store".into(), self.episodic_store.snapshot()), - ("episodic_retrieve".into(), self.episodic_retrieve.snapshot()), + ( + "episodic_retrieve".into(), + self.episodic_retrieve.snapshot(), + ), ("graph_bfs".into(), self.graph_bfs.snapshot()), ("spatial_path".into(), self.spatial_path.snapshot()), ("xai_api_call".into(), self.xai_api_call.snapshot()), @@ -313,7 +317,8 @@ pub fn run_osint_audit( )); } if episodic_saturation > 90.0 { - recommendations.push("Episodic memory >90% full — consider increasing capacity or pruning".into()); + recommendations + .push("Episodic memory >90% full — consider increasing capacity or pruning".into()); } if deleted as f32 / (graph_triplet_count.max(1) as f32) > 0.3 { recommendations.push("30%+ triplets soft-deleted — consider compaction".into()); @@ -322,7 +327,9 @@ pub fn run_osint_audit( recommendations.push("xAI API failure rate >50% — check API key and network".into()); } if nars_deductions.calls == 0 && graph_active_count > 10 { - recommendations.push("No NARS deductions run yet — call infer_deductions() to expand knowledge".into()); + recommendations.push( + "No NARS deductions run yet — call infer_deductions() to expand knowledge".into(), + ); } let timestamp_ms = std::time::SystemTime::now() @@ -372,7 +379,11 @@ mod tests { r.xai_api_call.record_success(50_000_000, 0); let health = r.snapshot(); assert_eq!(health.stages.len(), 12); - let extraction = health.stages.iter().find(|(n, _)| n == "extraction").unwrap(); + let extraction = health + .stages + .iter() + .find(|(n, _)| n == "extraction") + .unwrap(); assert_eq!(extraction.1.calls, 1); } @@ -384,7 +395,12 @@ mod tests { assert_eq!(result.graph.deleted_triplets, 20); assert_eq!(result.graph.contradictions, 2); assert!(!result.recommendations.is_empty()); - assert!(result.recommendations.iter().any(|r| r.contains("contradiction"))); + assert!( + result + .recommendations + .iter() + .any(|r| r.contains("contradiction")) + ); } #[test] @@ -408,6 +424,9 @@ mod tests { let r2 = osint_registry(); // Same singleton r1.extraction.record_success(100, 1); - assert_eq!(r2.extraction.snapshot().calls, r1.extraction.snapshot().calls); + assert_eq!( + r2.extraction.snapshot().calls, + r1.extraction.snapshot().calls + ); } } diff --git a/crates/stubs/notebook-query/src/reasoning.rs b/crates/stubs/notebook-query/src/reasoning.rs index 82791443a..d4a5d81cc 100644 --- a/crates/stubs/notebook-query/src/reasoning.rs +++ b/crates/stubs/notebook-query/src/reasoning.rs @@ -5,8 +5,8 @@ //! - NARS abductive inference: infer edges from chain walks //! - Progressive hydration lens: ZeckF64 resolution slider -use std::collections::HashMap; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; // ============================================================================= // NARS Truth Values @@ -94,11 +94,7 @@ pub enum InferenceType { /// /// For every A→B→C chain where both edges have confidence > `min_confidence`, /// infer A→C with deduction truth. -pub fn infer_edges( - edges: &[TruthEdge], - min_confidence: f64, - max_hops: usize, -) -> Vec { +pub fn infer_edges(edges: &[TruthEdge], min_confidence: f64, max_hops: usize) -> Vec { // Build adjacency: source → [(target, truth, rel_type)] let mut adj: HashMap<&str, Vec<(&str, &TruthValue, &str)>> = HashMap::new(); for e in edges { @@ -149,7 +145,9 @@ pub fn infer_edges( let mut rev: HashMap<&str, Vec<(&str, &TruthValue)>> = HashMap::new(); for e in edges { if !e.inferred && e.truth.confidence >= min_confidence { - rev.entry(&e.target).or_default().push((&e.source, &e.truth)); + rev.entry(&e.target) + .or_default() + .push((&e.source, &e.truth)); } } @@ -276,19 +274,31 @@ pub struct ResolutionLevel { impl ResolutionLevel { pub fn scent_only() -> Self { - Self { bytes: 1, threshold: 3 } + Self { + bytes: 1, + threshold: 3, + } } pub fn low() -> Self { - Self { bytes: 2, threshold: 4 } + Self { + bytes: 2, + threshold: 4, + } } pub fn medium() -> Self { - Self { bytes: 4, threshold: 5 } + Self { + bytes: 4, + threshold: 5, + } } pub fn full() -> Self { - Self { bytes: 8, threshold: 6 } + Self { + bytes: 8, + threshold: 6, + } } } @@ -304,10 +314,10 @@ pub fn filter_edges_by_resolution( // (real ZeckF64 would use the packed edge bytes, but we don't have them // in the JSON layer — bgz17 works at the container level) let threshold = match _resolution.bytes { - 1 => 0.0, // show everything - 2 => 0.3, // filter weak edges - 4 => 0.5, // medium filter - _ => 0.7, // strong edges only + 1 => 0.0, // show everything + 2 => 0.3, // filter weak edges + 4 => 0.5, // medium filter + _ => 0.7, // strong edges only }; edges @@ -394,14 +404,22 @@ mod tests { fn test_resolution_filter() { let edges = vec![ TruthEdge { - source: "A".into(), target: "B".into(), rel_type: "KNOWS".into(), + source: "A".into(), + target: "B".into(), + rel_type: "KNOWS".into(), truth: TruthValue::new(0.95, 0.87), - inferred: false, via: vec![], inference_type: None, + inferred: false, + via: vec![], + inference_type: None, }, TruthEdge { - source: "C".into(), target: "D".into(), rel_type: "MAYBE".into(), + source: "C".into(), + target: "D".into(), + rel_type: "MAYBE".into(), truth: TruthValue::new(0.20, 0.15), - inferred: false, via: vec![], inference_type: None, + inferred: false, + via: vec![], + inference_type: None, }, ]; @@ -433,22 +451,29 @@ mod tests { fn test_infer_edges_deduction() { let edges = vec![ TruthEdge { - source: "Palantir".into(), target: "US_DoD".into(), + source: "Palantir".into(), + target: "US_DoD".into(), rel_type: "DEVELOPED_BY".into(), truth: TruthValue::new(0.95, 0.87), - inferred: false, via: vec![], inference_type: None, + inferred: false, + via: vec![], + inference_type: None, }, TruthEdge { - source: "US_DoD".into(), target: "Gotham".into(), + source: "US_DoD".into(), + target: "Gotham".into(), rel_type: "DEPLOYED_BY".into(), truth: TruthValue::new(0.90, 0.82), - inferred: false, via: vec![], inference_type: None, + inferred: false, + via: vec![], + inference_type: None, }, ]; let inferred = infer_edges(&edges, 0.5, 2); assert!(!inferred.is_empty()); - let palantir_gotham = inferred.iter() + let palantir_gotham = inferred + .iter() .find(|e| e.source == "Palantir" && e.target == "Gotham"); assert!(palantir_gotham.is_some()); let edge = palantir_gotham.unwrap(); @@ -459,22 +484,31 @@ mod tests { #[test] fn test_seal_status() { let prev = GraphSnapshot { - version: 0, name: "base".into(), - nodes: vec![], edges: vec![], + version: 0, + name: "base".into(), + nodes: vec![], + edges: vec![], seal_status: SealStatus::Staunen, - new_edge_count: 10, revised_edge_count: 0, + new_edge_count: 10, + revised_edge_count: 0, }; let same = GraphSnapshot { - version: 1, name: "no change".into(), - nodes: vec![], edges: vec![], + version: 1, + name: "no change".into(), + nodes: vec![], + edges: vec![], seal_status: SealStatus::Wisdom, - new_edge_count: 0, revised_edge_count: 0, + new_edge_count: 0, + revised_edge_count: 0, }; let learning = GraphSnapshot { - version: 2, name: "enrichment".into(), - nodes: vec![], edges: vec![], + version: 2, + name: "enrichment".into(), + nodes: vec![], + edges: vec![], seal_status: SealStatus::Staunen, - new_edge_count: 5, revised_edge_count: 3, + new_edge_count: 5, + revised_edge_count: 3, }; assert_eq!(compute_seal_status(&prev, &same), SealStatus::Wisdom); diff --git a/crates/stubs/notebook-query/src/thinking.rs b/crates/stubs/notebook-query/src/thinking.rs index 23e63f695..2bc71e04b 100644 --- a/crates/stubs/notebook-query/src/thinking.rs +++ b/crates/stubs/notebook-query/src/thinking.rs @@ -44,10 +44,8 @@ pub async fn execute_think(source: &str) -> Result { let graph = build_thinking_graph(); let t0 = std::time::Instant::now(); - let mut session = Session::new_from_task( - format!("think-{}", uuid::Uuid::new_v4()), - "sensory_ingest", - ); + let mut session = + Session::new_from_task(format!("think-{}", uuid::Uuid::new_v4()), "sensory_ingest"); session.context.set("raw_input", source.to_string()).await; // Execute all layers until completion (max 50 steps for safety) @@ -100,7 +98,9 @@ mod tests { #[tokio::test] async fn test_execute_think() { - let result = execute_think("MATCH (n:System) RETURN n.name").await.unwrap(); + let result = execute_think("MATCH (n:System) RETURN n.name") + .await + .unwrap(); assert!(!result.output.is_empty()); assert!(result.layers_executed >= 6); assert!(!result.band.is_empty()); diff --git a/crates/stubs/notebook-query/tests/gremlin_e2e.rs b/crates/stubs/notebook-query/tests/gremlin_e2e.rs index 69b6c694b..1bbf46129 100644 --- a/crates/stubs/notebook-query/tests/gremlin_e2e.rs +++ b/crates/stubs/notebook-query/tests/gremlin_e2e.rs @@ -4,7 +4,7 @@ //! query reached real lance-graph execution ("→ Cypher" in the output) or fell //! back to the graph echo (a lance-graph Cypher-coverage gap — still safe). -use notebook_query::{execute, QueryLanguage}; +use notebook_query::{QueryLanguage, execute}; fn aiwar_data_path() -> Option<&'static str> { [ @@ -27,13 +27,18 @@ fn gremlin_has_label_executes_or_falls_back_cleanly() { let q = "g.V().hasLabel('System').limit(3)"; let res = execute(q, QueryLanguage::Gremlin).expect("execute returns Ok"); - eprintln!("--- gremlin e2e ---\nquery: {q}\nraw_output:\n{}", res.raw_output); + eprintln!( + "--- gremlin e2e ---\nquery: {q}\nraw_output:\n{}", + res.raw_output + ); assert!(res.graph_json.is_some(), "graph_json should be present"); if res.raw_output.contains("→ Cypher") { eprintln!("RESULT: real lance-graph Gremlin execution CONFIRMED"); } else { - eprintln!("RESULT: fell back to graph echo (lance-graph Cypher coverage gap for this shape)"); + eprintln!( + "RESULT: fell back to graph echo (lance-graph Cypher coverage gap for this shape)" + ); } } @@ -49,7 +54,10 @@ fn gremlin_multihop_path_traversal_runs() { // The cockpit's headline traversal shape (real label that exists in the data). let q = "g.V().hasLabel('System').outE().inV().path()"; let res = execute(q, QueryLanguage::Gremlin).expect("execute returns Ok"); - eprintln!("--- multi-hop ---\nquery: {q}\nraw_output:\n{}", res.raw_output); + eprintln!( + "--- multi-hop ---\nquery: {q}\nraw_output:\n{}", + res.raw_output + ); assert!(res.graph_json.is_some(), "graph_json should be present"); // With the Entity inheritance root, the untyped target binds and the // multi-hop pattern reaches real execution (no "n1__id" projection failure). @@ -95,7 +103,10 @@ fn gremlin_returns_dictionary_encoded_label_column() { // back to its string value transparently, so the codebook table is queryable. let q = "g.V().hasLabel('System').values('type')"; let res = execute(q, QueryLanguage::Gremlin).expect("execute returns Ok"); - eprintln!("--- dict column ---\nquery: {q}\nraw_output:\n{}", res.raw_output); + eprintln!( + "--- dict column ---\nquery: {q}\nraw_output:\n{}", + res.raw_output + ); assert!( res.raw_output.contains("→ Cypher"), "RETURN over a dictionary-encoded codebook column should reach real lance-graph execution, got: {}", @@ -155,7 +166,10 @@ fn entity_type_column_is_the_canonical_codebook() { // Arrow dictionary. It must be queryable through the real engine. let q = "MATCH (n:Entity) RETURN n.entity_type LIMIT 5"; match execute(q, QueryLanguage::Cypher) { - Ok(res) => eprintln!("--- entity_type (canonical EntityTypeId) ---\n{}", res.raw_output), + Ok(res) => eprintln!( + "--- entity_type (canonical EntityTypeId) ---\n{}", + res.raw_output + ), Err(e) => panic!("entity_type codebook column should be queryable, got: {e}"), } } @@ -176,7 +190,10 @@ fn reltype_column_is_the_canonical_link_codebook() { // Entity root (anonymous `()` defaults to an unregistered `Node` label). let q = "MATCH (a:Entity)-[r:Edge]->(b:Entity) RETURN r.reltype LIMIT 5"; match execute(q, QueryLanguage::Cypher) { - Ok(res) => eprintln!("--- reltype (canonical link codebook) ---\n{}", res.raw_output), + Ok(res) => eprintln!( + "--- reltype (canonical link codebook) ---\n{}", + res.raw_output + ), Err(e) => panic!("reltype codebook column should be queryable, got: {e}"), } } diff --git a/crates/stubs/q2-ndarray/src/deepnsm.rs b/crates/stubs/q2-ndarray/src/deepnsm.rs index 93730a8b2..8684686b7 100644 --- a/crates/stubs/q2-ndarray/src/deepnsm.rs +++ b/crates/stubs/q2-ndarray/src/deepnsm.rs @@ -366,11 +366,53 @@ pub struct NsmLegality { /// Approved semantic molecules (frequently used complex concepts that are /// accepted in NSM explications even though they are not primes). const MOLECULES: &[&str] = &[ - "hands", "eyes", "mouth", "head", "face", "ears", "nose", "legs", "arms", "heart", "mind", - "children", "men", "women", "animal", "dog", "cat", "bird", "fish", "tree", "ground", "sun", - "moon", "day", "night", "morning", "evening", "long_time", "short_time", "hot", "cold", - "hard", "soft", "round", "flat", "sharp", "heavy", "light", "wet", "dry", "colour", "white", - "black", "red", "green", "blue", "yellow", + "hands", + "eyes", + "mouth", + "head", + "face", + "ears", + "nose", + "legs", + "arms", + "heart", + "mind", + "children", + "men", + "women", + "animal", + "dog", + "cat", + "bird", + "fish", + "tree", + "ground", + "sun", + "moon", + "day", + "night", + "morning", + "evening", + "long_time", + "short_time", + "hot", + "cold", + "hard", + "soft", + "round", + "flat", + "sharp", + "heavy", + "light", + "wet", + "dry", + "colour", + "white", + "black", + "red", + "green", + "blue", + "yellow", ]; /// Analyse an NSM explication for legality with respect to a target word. @@ -458,7 +500,10 @@ mod tests { // "to" is not in vocab → should not contribute // All values should be non-negative and sum to ~1.0 (L1-normalised). let sum: f32 = v.iter().sum(); - assert!((sum - 1.0).abs() < 1e-5, "L1 norm should be ~1.0, got {sum}"); + assert!( + (sum - 1.0).abs() < 1e-5, + "L1 norm should be ~1.0, got {sum}" + ); } #[test] @@ -545,7 +590,10 @@ mod tests { fn test_legality_with_molecules() { let result = analyze_legality("someone feels something good in the head", "joy"); assert!(!result.uses_original_word); - assert!(result.molecule_tokens > 0, "should detect 'head' as molecule"); + assert!( + result.molecule_tokens > 0, + "should detect 'head' as molecule" + ); } #[test] diff --git a/crates/stubs/q2-ndarray/src/lib.rs b/crates/stubs/q2-ndarray/src/lib.rs index dd116afe4..a7f9951e0 100644 --- a/crates/stubs/q2-ndarray/src/lib.rs +++ b/crates/stubs/q2-ndarray/src/lib.rs @@ -77,8 +77,7 @@ pub fn ones(rows: usize, cols: usize) -> Array2D { /// Build a 2D array from a flat `Vec`. #[cfg(feature = "ndarray-simd")] pub fn from_vec(rows: usize, cols: usize, data: Vec) -> Result { - ndarray::Array2::from_shape_vec((rows, cols), data) - .map_err(|e| format!("Shape error: {e}")) + ndarray::Array2::from_shape_vec((rows, cols), data).map_err(|e| format!("Shape error: {e}")) } // ── Fallback: pure-Rust scalar (no ndarray dependency) ──────────────────────