mecha10_core/context/blackboard.rs
1//! Access to the per-tree, non-persistent, in-memory scratch store.
2
3use super::Context;
4use crate::blackboard::Blackboard;
5
6impl Context {
7 /// Get this context's [`Blackboard`] - a synchronous, in-memory key/value scratch store
8 /// shared by sibling nodes within a single running behavior tree.
9 ///
10 /// Unlike `ctx.state_manager()`, this is **not** persisted anywhere; it exists only for the
11 /// lifetime of this `Context` (and is lost on process restart). Use it for cheap scratch
12 /// data that sibling nodes need to share during a single run of a tree, without a pub/sub
13 /// topic round-trip through `message_bus`.
14 ///
15 /// # Example
16 ///
17 /// ```rust,no_run
18 /// # use mecha10_core::prelude::*;
19 /// # async fn example(ctx: &Context) {
20 /// ctx.blackboard().set("last_target", serde_json::json!({"x": 1.0, "y": 2.0}));
21 ///
22 /// if let Some(target) = ctx.blackboard().get("last_target") {
23 /// println!("target: {target}");
24 /// }
25 /// # }
26 /// ```
27 pub fn blackboard(&self) -> &Blackboard {
28 &self.blackboard
29 }
30}
31
32// Without the "messaging" feature, `Context::new` skips the infrastructure-config/Redis
33// connection path entirely (see the `#[cfg(feature = "messaging")]` gating in
34// `context::lifecycle::Context::new`), so it builds without any real infra present. With
35// "messaging" enabled it requires a real `config/infrastructure.<env>.yaml` (or
36// `MECHA10_REDIS_URL`/`mecha10.json`) and a reachable Redis - neither of which exist in this
37// repo/CI - so these tests follow the same precedent as `node::tests` (see `src/node.rs`) and
38// only run in the messaging-less build.
39#[cfg(all(test, not(feature = "messaging")))]
40mod tests {
41 use super::*;
42
43 #[tokio::test]
44 async fn fresh_context_blackboard_starts_empty() {
45 let ctx = Context::new("blackboard-test-node")
46 .await
47 .expect("context should build without messaging");
48
49 assert!(ctx.blackboard().is_empty());
50 assert_eq!(ctx.blackboard().len(), 0);
51 }
52
53 #[tokio::test]
54 async fn context_clone_shares_the_same_blackboard() {
55 let ctx = Context::new("blackboard-test-node-clone")
56 .await
57 .expect("context should build without messaging");
58 let cloned = ctx.clone();
59
60 ctx.blackboard().set("key", serde_json::json!("value"));
61
62 assert_eq!(cloned.blackboard().get("key"), Some(serde_json::json!("value")));
63 }
64}