Skip to content

Commit 03ff307

Browse files
committed
docs(contract): two runnable Elixir-style orchestration deep-dive examples
examples/cognitive_cycle.rs — the active-inference loop: a ThoughtCtx piped through a tactic recipe (Elixir |> over the uniform `Tactic` behaviour), marker-gated (Gate-bucket tactics skip in FLOW), looping until free energy descends below the homeostasis floor ("shader rests"). Runs to rest in 2 rounds; narrates every step (which tactic fired, bucket, Δconf, gate state). examples/savant_dispatch.rs — Odoo savant delegation: AXIS-A guard (woa-rs) delegates the ambiguous core to a savant; dispatch is the data tuple (family · ReasoningKind · InferenceType→QueryStrategy · Semiring · Style), pattern-matched on ReasoningKind (Elixir case). Real savants from the roster (PaymentToInvoiceMatcher / AutopostRecommender / FiscalPositionResolver / SequenceGapAnomalyDetector); output is a NARS suggestion, never an un-guarded write (Iron Rule 7). Both `cargo run -p lance-graph-contract --example <name>` — genuine (every call is a real contract kernel/type), self-explanatory, for a quick deep-dive. https://claude.ai/code/session_017GFLBnDy23AWBqvkbHHC41
1 parent 057cce3 commit 03ff307

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! Orchestration deep-dive #1 — the active-inference cognitive cycle.
2+
//!
3+
//! Run it: `cargo run -p lance-graph-contract --example cognitive_cycle`
4+
//!
5+
//! THE IDEA (Elixir-style):
6+
//! * every reasoning tactic implements ONE behaviour — `Tactic` (meta/gate/apply/run),
7+
//! exactly like an Elixir `@behaviour` / GenServer callback.
8+
//! * a "recipe" is a *pipeline* of tactics (the Elixir `|>` operator), applied in order.
9+
//! * each tactic is **clock-gated** by markers: a Gate-bucket tactic only fires when there
10+
//! is surprise to act on (CollapseGate SD not in FLOW). Most stay dark — like a CPU.
11+
//! * the loop runs until free energy descends below the homeostasis floor: "the shader
12+
//! can't resist the thinking… F descends, bits clear, shader rests."
13+
//!
14+
//! Nothing here is a toy: every call is a real `lance_graph_contract` kernel.
15+
16+
use lance_graph_contract::recipe_kernels::{kernel, GateState, ThoughtCtx, SD_FLOW};
17+
use lance_graph_contract::recipes::recipe_by_code;
18+
19+
/// A "deep-think" recipe = an ordered pipeline of tactics (by their catalogue code).
20+
/// Read it top-to-bottom like an Elixir pipe:
21+
/// ctx |> Expand |> Decompose |> Prune |> Contradiction |> Converge |> Meta |> Filter |> Reduce
22+
const DEEP_THINK: [&str; 8] = ["RTE", "HTD", "TCP", "CR", "CDT", "MCP", "TCF", "CUR"];
23+
24+
fn gate_state(sd: f32) -> GateState {
25+
if sd < SD_FLOW { GateState::Flow } else if sd <= 0.35 { GateState::Hold } else { GateState::Block }
26+
}
27+
28+
fn main() {
29+
// A *surprised* starting state: high dispersion (SD=BLOCK), high free energy,
30+
// four candidate interpretations, and a same-topic contradiction in the belief set.
31+
let mut ctx = ThoughtCtx::new(vec![0.92, 0.61, 0.34, 0.12]);
32+
ctx.sd = 0.42; // > 0.35 → BLOCK: the gate is wide open, deep tactics will fire
33+
ctx.free_energy = 0.80;
34+
ctx.confidence = 0.50;
35+
ctx.beliefs = vec![(7, 0.90, 0.8), (7, 0.10, 0.7)]; // topic 7 asserted true AND false
36+
37+
println!("== cognitive cycle: one Think, run to rest ==\n");
38+
println!("start: gate={:?} F={:.2} conf={:.2} candidates={} beliefs={}\n",
39+
gate_state(ctx.sd), ctx.free_energy, ctx.confidence, ctx.candidates.len(), ctx.beliefs.len());
40+
41+
// The active-inference loop: keep thinking while there is surprise (gate != FLOW).
42+
let mut round = 0;
43+
while gate_state(ctx.sd) != GateState::Flow && round < 5 {
44+
round += 1;
45+
println!("── round {round} (gate {:?}) ───────────────────────", gate_state(ctx.sd));
46+
47+
// Pipe the context through the recipe. Each step is `ctx |> tactic`.
48+
for code in DEEP_THINK {
49+
let rec = recipe_by_code(code).expect("recipe in catalogue");
50+
let k = kernel(rec.id).expect("kernel for id");
51+
let out = k.run(&mut ctx); // gate + apply; confidence auto-clamped
52+
println!(
53+
" {:<5} [{:<12}] {:<22} {} (Δconf {:+.2})",
54+
code,
55+
format!("{:?}", rec.bucket),
56+
rec.name,
57+
if out.fired { out.note } else { "· gated off (FLOW)" },
58+
out.delta_conf,
59+
);
60+
}
61+
62+
// The cycle resolved some surprise: dispersion + free energy anneal toward rest.
63+
// (In the wired system this falls out of the codec sweep; here we make it explicit.)
64+
ctx.sd *= 0.55;
65+
ctx.free_energy *= 0.5;
66+
println!(" → after round: gate={:?} SD={:.3} F={:.3} conf={:.2}\n",
67+
gate_state(ctx.sd), ctx.sd, ctx.free_energy, ctx.confidence);
68+
}
69+
70+
println!("== rest == the shader stopped because gate reached {:?} (SD={:.3} < FLOW {SD_FLOW}).",
71+
gate_state(ctx.sd), ctx.sd);
72+
println!("final: conf={:.2}, {} candidate(s) survived pruning, {} beliefs.",
73+
ctx.confidence, ctx.candidates.len(), ctx.beliefs.len());
74+
println!("\nKey: Gate-bucket tactics (TCP/CDT/TCF/CUR) skip while in FLOW — the markers,");
75+
println!("not a scheduler, decide what fires. Same `Tactic` behaviour, 34 hot-swappable units.");
76+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! Orchestration deep-dive #2 — Odoo savant delegation (woa-rs AXIS-A guard → lance-graph AXIS-B reason).
2+
//!
3+
//! Run it: `cargo run -p lance-graph-contract --example savant_dispatch`
4+
//!
5+
//! THE IDEA (Elixir-style):
6+
//! * a Savant is a delegated reasoner declared as a *tuple* (family · ReasoningKind ·
7+
//! InferenceType · SemiringChoice · StyleCluster) — the tuple FULLY determines dispatch.
8+
//! * dispatch is **pattern-matching** on `ReasoningKind` (like an Elixir `case`/multi-clause
9+
//! function head), and the `InferenceType` resolves O(1) to a `QueryStrategy`.
10+
//! * the deterministic guard (AXIS-A) stays in woa-rs; only the *ambiguous core* is delegated
11+
//! here (AXIS-B), and the answer is a NARS-truth-weighted **suggestion**, never an
12+
//! un-guarded write (Iron Rule 7).
13+
14+
use lance_graph_contract::reasoning::ReasoningKind;
15+
use lance_graph_contract::savants::{savant_by_name, Savant};
16+
17+
/// A concrete situation arriving from the ERP.
18+
struct Situation {
19+
headline: &'static str,
20+
/// The AXIS-A guard already ran in woa-rs; `true` means it could NOT decide deterministically
21+
/// and is delegating the ambiguous core to the savant.
22+
ambiguous: bool,
23+
savant: &'static str,
24+
}
25+
26+
/// Pattern-match the kind → the reasoning approach (the Elixir multi-clause `case`).
27+
fn approach(kind: ReasoningKind) -> &'static str {
28+
match kind {
29+
ReasoningKind::CustomerCategory => "classify against the family codebook (deductive lookup)",
30+
ReasoningKind::PostingAnomaly => "abduce the most likely cause from the evidence trail",
31+
ReasoningKind::NextBestAction => "induce the action with the highest expected value",
32+
ReasoningKind::InvoiceCompleteness => "check required-field coverage, score the gaps",
33+
ReasoningKind::MailIntent => "resonate the message against intent prototypes",
34+
ReasoningKind::Other(code) => match code {
35+
5 | 6 => "match open items / bank lines by evidence fusion (reconcile)",
36+
_ => "domain-specific Other(code) reasoner",
37+
},
38+
}
39+
}
40+
41+
fn dispatch(s: &Savant) {
42+
println!(" savant {} (#{}, lane {})", s.name, s.id, s.lane);
43+
println!(" family {}", s.family.map(|f| format!("0x{f:02X}")).unwrap_or_else(|| "None (needs alignment axiom)".into()));
44+
println!(" tuple kind={:?} · infer={:?} · semiring={:?} · style={:?}", s.kind, s.inference, s.semiring, s.style);
45+
// The InferenceType resolves O(1) to the runtime query strategy.
46+
println!(" → strategy {:?} (InferenceType::default_strategy)", s.query_strategy());
47+
println!(" → approach {}", approach(s.kind));
48+
println!(" → output NARS (frequency, confidence) suggestion — woa-rs applies it behind its AXIS-A guard\n");
49+
}
50+
51+
fn main() {
52+
println!("== Odoo savant delegation: AXIS-A guard (woa-rs) → AXIS-B reason (lance-graph) ==\n");
53+
54+
let inbox = [
55+
Situation { headline: "€1,200 payment arrived — does it fully reconcile the partner's open invoices?", ambiguous: true, savant: "PaymentToInvoiceMatcher" },
56+
Situation { headline: "3rd identical bill from this vendor, unmodified — auto-post it?", ambiguous: true, savant: "AutopostRecommender" },
57+
Situation { headline: "new B2B partner in AT — which fiscal position (tax mapping)?", ambiguous: true, savant: "FiscalPositionResolver" },
58+
Situation { headline: "journal sequence jumps 1042 → 1044 — is 1043 a deleted posted entry?", ambiguous: true, savant: "SequenceGapAnomalyDetector" },
59+
Situation { headline: "invoice with a perfectly matching single open item", ambiguous: false, savant: "ReconcileMatchSelector" },
60+
];
61+
62+
for s in &inbox {
63+
println!("• {}", s.headline);
64+
if !s.ambiguous {
65+
println!(" AXIS-A (woa-rs): deterministic match — applied directly, NO delegation.\n");
66+
continue;
67+
}
68+
let sv = savant_by_name(s.savant).expect("savant in roster");
69+
dispatch(sv);
70+
}
71+
72+
println!("Same delegation tuple for all 25 savants; dispatch is data, not branches.");
73+
println!("Pattern-match on ReasoningKind picks the approach; the family's StyleCluster colours it.");
74+
}

0 commit comments

Comments
 (0)