use std::sync::Arc;
use khive_pack_brain::BrainPack;
use khive_pack_kg::KgPack;
use khive_runtime::{DispatchHook, KhiveRuntime, Namespace, PackRuntime, VerbRegistryBuilder};
use serde_json::json;
async fn promote_namespace(brain: &BrainPack, rt: &KhiveRuntime, namespace: &str) {
use khive_runtime::{Namespace, VerbRegistryBuilder};
let registry = VerbRegistryBuilder::new()
.build()
.expect("minimal registry for promotion");
let ns = Namespace::parse(namespace).expect("valid namespace string");
let token = rt.authorize(ns).expect("authorize namespace token");
brain
.dispatch("brain.profiles", json!({}), ®istry, &token)
.await
.expect("brain.profiles must succeed to promote namespace via production path");
}
#[tokio::test]
async fn dispatch_hook_fires_on_cold_namespace_no_prior_activation() {
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let brain = Arc::new(BrainPack::new(rt.clone()));
let mut builder = VerbRegistryBuilder::new();
builder.register(KgPack::new(rt.clone()));
let hook: Arc<dyn DispatchHook> = brain.clone();
builder.with_dispatch_hook(hook);
let registry = builder.build().expect("registry builds");
registry
.dispatch(
"create",
json!({
"kind": "entity",
"name": "ColdHookProbe",
"entity_kind": "concept"
}),
)
.await
.expect("create entity must succeed");
promote_namespace(&brain, &rt, "local").await;
let snap = brain.snapshot();
assert_eq!(
snap.balanced_recall.total_events, 1,
"active snapshot total_events must be 1 after cold-hook signal survives promotion; got {}",
snap.balanced_recall.total_events
);
}
#[tokio::test]
async fn dispatch_hook_applies_signals_per_namespace_independently() {
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let brain = Arc::new(BrainPack::new(rt.clone()));
let build_registry = |ns: &str| {
let rt2 = rt.clone();
let brain2 = brain.clone();
let ns_owned = ns.to_string();
let mut builder = VerbRegistryBuilder::new();
builder.register(KgPack::new(rt2));
builder.with_default_namespace(ns_owned);
let hook: Arc<dyn DispatchHook> = brain2;
builder.with_dispatch_hook(hook);
builder.build().expect("registry builds")
};
let reg_alpha = build_registry("ns-alpha");
let reg_beta = build_registry("ns-beta");
for i in 0..2u32 {
reg_alpha
.dispatch(
"create",
json!({"kind":"entity","name":format!("AlphaE{i}"),"entity_kind":"concept"}),
)
.await
.expect("alpha dispatch");
}
for i in 0..3u32 {
reg_beta
.dispatch(
"create",
json!({"kind":"entity","name":format!("BetaE{i}"),"entity_kind":"concept"}),
)
.await
.expect("beta dispatch");
}
promote_namespace(&brain, &rt, "ns-alpha").await;
let snap_alpha = brain.snapshot();
assert_eq!(
snap_alpha.balanced_recall.total_events, 2,
"ns-alpha active snapshot must show 2 events after promotion; got {}",
snap_alpha.balanced_recall.total_events
);
promote_namespace(&brain, &rt, "ns-beta").await;
let snap_beta = brain.snapshot();
assert_eq!(
snap_beta.balanced_recall.total_events, 3,
"ns-beta active snapshot must show 3 events after promotion; got {}",
snap_beta.balanced_recall.total_events
);
}
#[tokio::test]
async fn brain_pack_hook_does_not_fire_on_unknown_verb() {
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let brain = Arc::new(BrainPack::new(rt.clone()));
let mut builder = VerbRegistryBuilder::new();
builder.register(KgPack::new(rt.clone()));
let hook: Arc<dyn DispatchHook> = brain.clone();
builder.with_dispatch_hook(hook);
let registry = builder.build().expect("registry builds");
let _ = registry.dispatch("frobnicate_nonexistent", json!({})).await;
promote_namespace(&brain, &rt, "local").await;
let snap = brain.snapshot();
assert_eq!(
snap.balanced_recall.total_events, 0,
"failed dispatch must NOT fire the hook; total_events must remain 0 after promotion; got {}",
snap.balanced_recall.total_events
);
}
#[tokio::test]
async fn cold_hook_signal_applies_on_top_of_persisted_snapshot() {
use khive_runtime::Namespace;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let setup_registry = {
let mut builder = VerbRegistryBuilder::new();
builder.register(KgPack::new(rt.clone()));
builder.register(BrainPack::new(rt.clone()));
builder.build().expect("setup registry")
};
let entity_result = setup_registry
.dispatch(
"create",
json!({
"kind": "entity",
"name": "SnapshotProbeTarget",
"entity_kind": "concept"
}),
)
.await
.expect("create entity for feedback target");
let target_id = entity_result["id"]
.as_str()
.expect("created entity must have id")
.to_string();
let brain_a = BrainPack::new(rt.clone());
let empty_registry = VerbRegistryBuilder::new()
.build()
.expect("minimal registry");
let local_ns = Namespace::parse("local").expect("local namespace");
let local_token = rt.authorize(local_ns).expect("local token");
brain_a
.dispatch("brain.profiles", json!({}), &empty_registry, &local_token)
.await
.expect("promote local namespace in brain_a before feedback");
for _ in 0..5u32 {
brain_a
.dispatch(
"brain.feedback",
json!({ "target_id": target_id, "signal": "useful" }),
&empty_registry,
&local_token,
)
.await
.expect("brain.feedback must succeed");
}
let persisted_total = brain_a.snapshot().balanced_recall.total_events;
assert_eq!(
persisted_total, 5,
"after 5 feedback calls brain_a snapshot must show 5 total_events; got {persisted_total}"
);
drop(brain_a);
let brain_b = Arc::new(BrainPack::new(rt.clone()));
let mut hook_builder = VerbRegistryBuilder::new();
hook_builder.register(KgPack::new(rt.clone()));
hook_builder.with_default_namespace("local".to_string());
let hook_arc: Arc<dyn DispatchHook> = brain_b.clone();
hook_builder.with_dispatch_hook(hook_arc);
let hook_registry = hook_builder.build().expect("hook registry for brain_b");
hook_registry
.dispatch(
"create",
json!({"kind":"entity","name":"ColdReplayProbe","entity_kind":"concept"}),
)
.await
.expect("kg dispatch through brain_b hook must succeed");
brain_b
.dispatch("brain.profiles", json!({}), &empty_registry, &local_token)
.await
.expect("promote local namespace in brain_b via cold DB load");
let snap = brain_b.snapshot();
assert_eq!(
snap.balanced_recall.total_events,
persisted_total + 1,
"brain_b cold-reload must yield persisted total ({persisted_total}) + \
1 queued signal; got {}",
snap.balanced_recall.total_events
);
}
#[tokio::test]
async fn brain_feedback_rejects_visible_only_target_id() {
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let ns_primary = Namespace::parse("brain-primary-ns").unwrap();
let ns_foreign = Namespace::parse("brain-foreign-ns").unwrap();
let tok_foreign = rt.authorize(ns_foreign.clone()).unwrap();
let foreign_entity = rt
.create_entity(
&tok_foreign,
"concept",
None,
"ForeignTarget",
None,
None,
vec![],
)
.await
.unwrap();
let foreign_id = foreign_entity.id.as_hyphenated().to_string();
let tok_vis = rt
.authorize_with_visibility(ns_primary.clone(), vec![ns_foreign.clone()])
.unwrap();
let found = rt.get_entity(&tok_vis, foreign_entity.id).await;
assert!(
found.is_ok(),
"visible-set token must be able to read the foreign entity; got: {found:?}"
);
let brain = BrainPack::new(rt.clone());
let empty_registry = VerbRegistryBuilder::new().build().unwrap();
brain
.dispatch("brain.profiles", json!({}), &empty_registry, &tok_vis)
.await
.expect("promote primary namespace");
let err = brain
.dispatch(
"brain.feedback",
json!({ "target_id": foreign_id, "signal": "useful" }),
&empty_registry,
&tok_vis,
)
.await
.unwrap_err();
let msg = err.to_string();
assert!(
msg.to_lowercase().contains("not found"),
"brain.feedback with visible-only target_id must return NotFound; got: {msg}"
);
}