Skip to main content

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}