use super::*;
use khive_runtime::{
DispatchHook, KhiveRuntime, Namespace, NamespaceToken, PackRuntime, RuntimeError,
VerbRegistryBuilder,
};
use khive_types::HandlerDef;
use serde_json::json;
fn make_pack() -> (BrainPack, KhiveRuntime) {
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = BrainPack::new(rt.clone());
(pack, rt)
}
fn empty_registry() -> khive_runtime::VerbRegistry {
VerbRegistryBuilder::new()
.build()
.expect("empty registry builds successfully")
}
async fn create_test_entity(rt: &KhiveRuntime, token: &NamespaceToken) -> String {
let entity = rt
.create_entity(token, "concept", None, "test-target", None, None, vec![])
.await
.expect("create test entity");
entity.id.to_string()
}
#[tokio::test]
async fn dispatch_unknown_verb_returns_invalid_input() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let err = pack
.dispatch(
"brain.unknown",
json!({}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("brain.unknown"),
"expected verb name in error: {msg}"
);
} else {
panic!("expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn dispatch_reset_returns_true_and_increments_epoch() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.reset",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
assert_eq!(result["reset"], json!(true));
assert_eq!(result["exploration_epoch"], json!(1u64));
assert_eq!(result["profile_id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn dispatch_reset_no_args_resets_default_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.reset",
json!({}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.expect("reset with no args must succeed (defaults to balanced-recall-v1)");
assert_eq!(result["reset"], json!(true));
assert_eq!(result["profile_id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn dispatch_reset_nonexistent_profile_returns_not_found() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let err = pack
.dispatch(
"brain.reset",
json!({"profile_id": "ghost-profile"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::NotFound(_)),
"reset on nonexistent profile must return NotFound, got {err:?}"
);
}
#[tokio::test]
async fn dispatch_reset_archived_profile_returns_invalid_input() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let err = pack
.dispatch(
"brain.reset",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("archived") || msg.contains("terminal"),
"reset on archived profile must mention 'archived' or 'terminal'; got: {msg}"
);
} else {
panic!("reset on archived profile must return InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn dispatch_feedback_invalid_signal_returns_invalid_input() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let target = "00000000-0000-0000-0000-000000000001";
let err = pack
.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "bad_signal"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("bad_signal"),
"expected signal name in error: {msg}"
);
assert!(
msg.contains("valid"),
"expected hint about valid values: {msg}"
);
} else {
panic!("expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn dispatch_state_returns_snapshot_fields() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.state",
json!({}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
assert!(result.get("profiles").is_some(), "missing profiles");
assert!(
result.get("balanced_recall").is_some(),
"missing balanced_recall"
);
assert!(result.get("bindings").is_some(), "missing bindings");
}
#[tokio::test]
async fn dispatch_profiles_returns_default_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.profiles",
json!({}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
let profiles = result["profiles"].as_array().unwrap();
assert!(!profiles.is_empty(), "expected at least one profile");
assert_eq!(profiles[0]["id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn dispatch_profiles_filtered_by_lifecycle() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.profiles",
json!({"lifecycle": "active"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
let profiles = result["profiles"].as_array().unwrap();
for p in profiles {
assert_eq!(p["lifecycle"], json!("active"));
}
}
#[tokio::test]
async fn dispatch_profile_returns_profile_details() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.profile",
json!({"id": "balanced-recall-v1"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
assert_eq!(result["id"], json!("balanced-recall-v1"));
assert_eq!(result["state_class"], json!("Bayesian"));
assert_eq!(result["consumer_kind"], json!("recall"));
}
#[tokio::test]
async fn dispatch_profile_not_found_returns_not_found() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let err = pack
.dispatch(
"brain.profile",
json!({"id": "nonexistent"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap_err();
assert!(matches!(err, RuntimeError::NotFound(_)));
}
#[tokio::test]
async fn dispatch_resolve_returns_default_profile_for_recall() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.resolve",
json!({"consumer_kind": "recall"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
assert_eq!(result["resolved_profile_id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn dispatch_activate_and_deactivate_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["lifecycle"], json!("inactive"));
let state = pack
.dispatch(
"brain.profile",
json!({"id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(state["lifecycle"], json!("inactive"));
let result = pack
.dispatch(
"brain.activate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["lifecycle"], json!("active"));
}
#[tokio::test]
async fn dispatch_archive_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let result = pack
.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["lifecycle"], json!("archived"));
}
#[tokio::test]
async fn dispatch_activate_nonexistent_profile_returns_not_found() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let err = pack
.dispatch(
"brain.activate",
json!({"profile_id": "ghost-profile"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap_err();
assert!(matches!(err, RuntimeError::NotFound(_)));
}
#[tokio::test]
async fn dispatch_bind_and_resolve_explicit_binding() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "agent-x",
"consumer_kind": "recall"
}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["bound"], json!(true));
assert_eq!(result["actor"], json!("agent-x"));
let resolved = pack
.dispatch(
"brain.resolve",
json!({"actor": "agent-x", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(resolved["resolved_profile_id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn dispatch_bind_nonexistent_profile_returns_not_found() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let err = pack
.dispatch(
"brain.bind",
json!({"profile_id": "ghost", "consumer_kind": "recall"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap_err();
assert!(matches!(err, RuntimeError::NotFound(_)));
}
#[tokio::test]
async fn dispatch_unbind_removes_binding() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "agent-y", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
let result = pack
.dispatch(
"brain.unbind",
json!({"actor": "agent-y"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["unbound"], json!(1u64));
}
#[tokio::test]
async fn ue5_h1_invalid_lifecycle_error_lists_only_public_states() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.profiles",
json!({"lifecycle": "deleted"}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("active"),
"UE5-H1: error must list 'active'; got: {msg}"
);
assert!(
msg.contains("inactive"),
"UE5-H1: error must list 'inactive'; got: {msg}"
);
assert!(
msg.contains("archived"),
"UE5-H1: error must list 'archived'; got: {msg}"
);
assert!(
!msg.contains("defined"),
"UE5-H1: error must NOT expose internal 'defined' state; got: {msg}"
);
assert!(
!msg.contains("registered"),
"UE5-H1: error must NOT expose internal 'registered' state; got: {msg}"
);
} else {
panic!("UE5-H1: expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn ue5_h1_internal_lifecycle_values_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
for internal_state in ["defined", "registered"] {
let err = pack
.dispatch(
"brain.profiles",
json!({"lifecycle": internal_state}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"UE5-H1: internal lifecycle '{internal_state}' must be rejected, got {err:?}"
);
}
}
#[tokio::test]
async fn b_c1_archived_activate_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let err = pack
.dispatch(
"brain.activate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("terminal") || msg.contains("archived"),
"B-C1: error must mention 'terminal' or 'archived'; got: {msg}"
);
} else {
panic!("B-C1: expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn b_c1_archived_deactivate_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let err = pack
.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"B-C1: deactivate on archived must return InvalidInput, got {err:?}"
);
}
#[tokio::test]
async fn b_c1_active_to_archived_direct_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("deactivate") || msg.contains("inactive"),
"B-C1: active→archived error must hint at deactivate; got: {msg}"
);
} else {
panic!("B-C1: expected InvalidInput for active→archived, got {err:?}");
}
}
#[tokio::test]
async fn b_c1_inactive_to_archived_is_permitted() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let result = pack
.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
result["lifecycle"],
json!("archived"),
"B-C1: inactive→archived must succeed"
);
}
#[tokio::test]
async fn dispatch_unbind_uses_and_not_or() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "namespace": "ns-a", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "namespace": "ns-b", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
let result = pack
.dispatch(
"brain.unbind",
json!({"namespace": "ns-a", "profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
result["unbound"],
json!(1u64),
"should remove exactly one binding"
);
let state = pack.state.lock().unwrap();
let remaining: Vec<_> = state
.bindings
.iter()
.filter(|b| b.namespace == "ns-b")
.collect();
assert_eq!(remaining.len(), 1, "ns-b binding must survive the unbind");
}
#[tokio::test]
async fn dispatch_config_all_parameters() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.config",
json!({}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
let obj = result.as_object().unwrap();
assert!(obj.contains_key("recall::relevance_weight"));
assert!(obj.contains_key("recall::salience_weight"));
assert!(obj.contains_key("recall::temporal_weight"));
}
#[tokio::test]
async fn dispatch_config_single_parameter() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let result = pack
.dispatch(
"brain.config",
json!({"parameter": "recall::relevance_weight"}),
®istry,
&rt.authorize(Namespace::local()).unwrap(),
)
.await
.unwrap();
assert_eq!(result["parameter"], json!("recall::relevance_weight"));
let mean = result["mean"].as_f64().unwrap();
assert!((mean - 0.7).abs() < 1e-6);
}
#[tokio::test]
async fn test_356_profile_record_total_events_synced_after_feedback() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
for _ in 0..3 {
pack.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful"}),
®istry,
&token,
)
.await
.unwrap();
}
let snap = pack.snapshot();
let live_total = snap.balanced_recall.total_events;
let record_result = pack
.dispatch(
"brain.profile",
json!({"id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let record_total = record_result["total_events"].as_u64().unwrap();
assert_eq!(
live_total, record_total,
"#356 part-A: profile_record.total_events ({record_total}) must equal \
balanced_recall.total_events ({live_total}) after feedback calls"
);
assert_eq!(live_total, 3, "expected exactly 3 events from part A");
{
let mut state = pack.state.lock().unwrap();
state.balanced_recall.total_events += 7; }
let hook_event = {
use khive_types::{EventKind, SubstrateKind};
let mut e = khive_storage::event::Event::new(
"local",
"search",
EventKind::Audit,
SubstrateKind::Event,
"kg",
);
e.outcome = khive_types::EventOutcome::Success;
e
};
let hook_view = khive_runtime::EventView {
event: hook_event,
observations: Vec::new(),
};
pack.on_dispatch(&hook_view).await;
let after_hook = pack
.dispatch(
"brain.profile",
json!({"id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let after_total = after_hook["total_events"].as_u64().unwrap();
let live_after = pack.snapshot().balanced_recall.total_events;
assert_eq!(
after_total, live_after,
"#356 part-B: on_dispatch sync must correct desync; \
record shows {after_total}, live state shows {live_after}"
);
}
#[tokio::test]
async fn test_357_feedback_no_double_count() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
pack.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
pack.snapshot().balanced_recall.total_events,
1,
"#357 pre-hook: handle_feedback must fold exactly once"
);
let hook_event = {
use khive_types::{EventKind, SubstrateKind};
khive_storage::event::Event::new(
"local",
"brain.feedback",
EventKind::FeedbackExplicit,
SubstrateKind::Event,
"brain",
)
};
let hook_view = khive_runtime::EventView {
event: hook_event,
observations: Vec::new(),
};
pack.on_dispatch(&hook_view).await;
assert_eq!(
pack.snapshot().balanced_recall.total_events,
1,
"#357: total_events must remain 1 after on_dispatch(brain.feedback); \
guard absent if this reads 2"
);
}
#[tokio::test]
async fn test_295_reset_restores_domain_priors_not_uniform() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch("brain.profiles", json!({}), ®istry, &token)
.await
.expect("trigger namespace load");
let hook_event = |verb: &str| {
use khive_types::{EventKind, SubstrateKind};
let mut e = khive_storage::event::Event::new(
"local",
verb,
EventKind::Audit,
SubstrateKind::Event,
"kg",
);
e.outcome = khive_types::EventOutcome::Success;
e
};
for _ in 0..4 {
let view = khive_runtime::EventView {
event: hook_event("search"),
observations: Vec::new(),
};
pack.on_dispatch(&view).await;
}
let target = create_test_entity(&rt, &token).await;
for _ in 0..5 {
pack.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful"}),
®istry,
&token,
)
.await
.unwrap();
}
let before = pack.snapshot();
assert!(
before.balanced_recall.salience.alpha() > 2.0,
"salience.alpha() must have grown past prior after useful feedback"
);
assert!(
before.balanced_recall.total_events >= 9,
"expected at least 9 total events (4 hook + 5 feedback), got {}",
before.balanced_recall.total_events
);
let pre_reset_record = pack
.dispatch(
"brain.profile",
json!({"id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
pre_reset_record["total_events"].as_u64().unwrap(),
before.balanced_recall.total_events,
"#295 pre-reset: profile record total_events out of sync before reset"
);
let reset_result = pack
.dispatch(
"brain.reset",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(reset_result["reset"], json!(true));
let epoch_after = reset_result["exploration_epoch"].as_u64().unwrap();
assert!(
epoch_after > 0,
"#295: exploration_epoch must increment after reset"
);
let after = pack.snapshot();
assert!(
(after.balanced_recall.salience.alpha() - 2.0).abs() < 1e-12,
"#295: salience.alpha() must be 2.0 after reset, got {}",
after.balanced_recall.salience.alpha()
);
assert!(
(after.balanced_recall.salience.beta() - 8.0).abs() < 1e-12,
"#295: salience.beta() must be 8.0 after reset, got {}",
after.balanced_recall.salience.beta()
);
assert!(
(after.balanced_recall.temporal.alpha() - 1.0).abs() < 1e-12,
"#295: temporal.alpha() must be 1.0 after reset, got {}",
after.balanced_recall.temporal.alpha()
);
assert!(
(after.balanced_recall.temporal.beta() - 9.0).abs() < 1e-12,
"#295: temporal.beta() must be 9.0 after reset, got {}",
after.balanced_recall.temporal.beta()
);
assert!(
(after.balanced_recall.relevance.alpha() - 7.0).abs() < 1e-12,
"#295: relevance.alpha() must be 7.0 after reset"
);
let record = pack
.dispatch(
"brain.profile",
json!({"id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let record_total = record["total_events"].as_u64().unwrap();
assert_eq!(
record_total, after.balanced_recall.total_events,
"#295: profile record total_events ({record_total}) must match \
live state ({}) after reset",
after.balanced_recall.total_events
);
let record_epoch = record["exploration_epoch"].as_u64().unwrap();
assert_eq!(
record_epoch, epoch_after,
"#295: profile record exploration_epoch ({record_epoch}) must match \
reset result ({epoch_after})"
);
let snap = &record["state_snapshot"];
let sal_alpha = snap["salience"]["alpha"].as_f64().unwrap();
assert!(
(sal_alpha - 2.0).abs() < 1e-12,
"#295: brain.profile state_snapshot salience.alpha() must be 2.0 after reset, \
got {sal_alpha}"
);
}
#[tokio::test]
async fn brain_reset_rejects_unknown_kwargs() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.reset",
json!({"unknownkw": "oops"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"brain.reset with unknown kwargs must return InvalidInput, got: {err:?}"
);
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("brain.reset"),
"error message must mention brain.reset, got: {msg}"
);
}
}
#[tokio::test]
async fn brain_reset_accepts_empty_params() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch("brain.reset", json!({}), ®istry, &token)
.await
.expect("brain.reset() must succeed with empty params");
assert_eq!(result["reset"], json!(true));
}
#[tokio::test]
async fn test_355_posteriors_update_after_dispatch_via_hook() {
let (pack, _rt) = make_pack();
pack.activate_namespace_for_test("local");
let before = pack.snapshot();
let tmp_alpha_before = before.balanced_recall.temporal.alpha();
let tmp_beta_before = before.balanced_recall.temporal.beta();
let target_id = uuid::Uuid::new_v4();
let fast_hit_event = {
use khive_types::{EventKind, SubstrateKind};
let mut e = khive_storage::event::Event::new(
"local",
"recall",
EventKind::Audit,
SubstrateKind::Event,
"memory",
);
e.outcome = khive_types::EventOutcome::Success;
e.target_id = Some(target_id);
e.duration_us = 10_000; e
};
let view = khive_runtime::EventView {
event: fast_hit_event,
observations: Vec::new(),
};
pack.on_dispatch(&view).await;
let after_fast = pack.snapshot();
assert!(
(after_fast.balanced_recall.temporal.alpha() - (tmp_alpha_before + 1.0)).abs() < 1e-12,
"#355: fast recall hit must increment temporal.alpha() via hook: expected {}, got {}",
tmp_alpha_before + 1.0,
after_fast.balanced_recall.temporal.alpha()
);
assert!(
(after_fast.balanced_recall.temporal.beta() - tmp_beta_before).abs() < 1e-12,
"#355: fast hit must NOT increment temporal.beta()"
);
let slow_hit_event = {
use khive_types::{EventKind, SubstrateKind};
let mut e = khive_storage::event::Event::new(
"local",
"recall",
EventKind::Audit,
SubstrateKind::Event,
"memory",
);
e.outcome = khive_types::EventOutcome::Success;
e.target_id = Some(target_id);
e.duration_us = 100_000; e
};
let view2 = khive_runtime::EventView {
event: slow_hit_event,
observations: Vec::new(),
};
pack.on_dispatch(&view2).await;
let after_slow = pack.snapshot();
assert!(
(after_slow.balanced_recall.temporal.beta() - (tmp_beta_before + 1.0)).abs() < 1e-12,
"#355: slow recall hit must increment temporal.beta() via hook: expected {}, got {}",
tmp_beta_before + 1.0,
after_slow.balanced_recall.temporal.beta()
);
let miss_event = {
use khive_types::{EventKind, SubstrateKind};
let mut e = khive_storage::event::Event::new(
"local",
"recall",
EventKind::Audit,
SubstrateKind::Event,
"memory",
);
e.outcome = khive_types::EventOutcome::Success;
e
};
let view3 = khive_runtime::EventView {
event: miss_event,
observations: Vec::new(),
};
pack.on_dispatch(&view3).await;
let after_miss = pack.snapshot();
assert!(
(after_miss.balanced_recall.temporal.beta() - (tmp_beta_before + 2.0)).abs() < 1e-12,
"#355: recall miss must further increment temporal.beta(): expected {}, got {}",
tmp_beta_before + 2.0,
after_miss.balanced_recall.temporal.beta()
);
}
#[tokio::test]
async fn w4_c2_unbind_no_filter_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "agent-z", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
let err = pack
.dispatch("brain.unbind", json!({}), ®istry, &token)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("filter") || msg.contains("profile_id") || msg.contains("actor"),
"C2: zero-filter unbind must mention required filter; got: {msg}"
);
} else {
panic!("C2: zero-filter unbind must return InvalidInput, got {err:?}");
}
let state = pack.state.lock().unwrap();
assert!(
!state.bindings.is_empty(),
"C2: binding must survive the rejected unbind"
);
}
#[tokio::test]
async fn w4_c3_bind_archived_profile_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let err = pack
.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("archived"),
"C3: bind to archived profile must mention 'archived'; got: {msg}"
);
} else {
panic!("C3: bind to archived profile must return InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn w4_c3_resolve_skips_archived_binding() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch("brain.profiles", json!({}), ®istry, &token)
.await
.expect("trigger namespace load");
{
let mut state = pack.state.lock().unwrap();
state.bindings.push(khive_brain_core::ProfileBinding {
actor: "*".into(),
namespace: "*".into(),
consumer_kind: "recall".into(),
profile_id: "balanced-recall-v1".into(),
priority: 100,
created_at: chrono::Utc::now(),
});
state
.profiles
.get_mut("balanced-recall-v1")
.unwrap()
.lifecycle = khive_brain_core::ProfileLifecycle::Archived;
}
let err = pack
.dispatch(
"brain.resolve",
json!({"consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::NotFound(_)),
"C3: resolve with only archived binding must return NotFound, got {err:?}"
);
}
#[tokio::test]
async fn w4_c4_feedback_rejects_nonexistent_target() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.feedback",
json!({"target_id": "00000000-0000-0000-0000-000000000000", "signal": "useful"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::NotFound(_)),
"C4: feedback with nonexistent target_id must return NotFound, got {err:?}"
);
}
#[tokio::test]
async fn w4_c4_feedback_rejects_nonexistent_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let err = pack
.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful", "served_by_profile_id": "fake-profile-xyz"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::NotFound(_)),
"C4: feedback with nonexistent served_by_profile_id must return NotFound, got {err:?}"
);
}
#[tokio::test]
async fn w4_c4_feedback_accepts_valid_target_and_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let result = pack
.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful", "served_by_profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["emitted"], json!(true));
assert_eq!(result["signal"], json!("useful"));
}
#[tokio::test]
async fn w4_h1_create_profile_creates_new_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch(
"brain.create_profile",
json!({"name": "my-profile-v1", "consumer_kind": "search", "description": "Custom search profile"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["created"], json!(true));
assert_eq!(result["profile_id"], json!("my-profile-v1"));
assert_eq!(result["lifecycle"], json!("inactive"));
assert_eq!(result["consumer_kind"], json!("search"));
let profiles = pack
.dispatch("brain.profiles", json!({}), ®istry, &token)
.await
.unwrap();
let ids: Vec<&str> = profiles["profiles"]
.as_array()
.unwrap()
.iter()
.filter_map(|p| p["id"].as_str())
.collect();
assert!(
ids.contains(&"my-profile-v1"),
"new profile must appear in brain.profiles"
);
}
#[tokio::test]
async fn w4_h1_create_profile_duplicate_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"H1: duplicate profile name must return InvalidInput, got {err:?}"
);
}
#[tokio::test]
async fn w4_h2_bindings_lists_rows() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch("brain.bindings", json!({}), ®istry, &token)
.await
.unwrap();
assert_eq!(result["count"], json!(0u64));
assert_eq!(result["bindings"], json!([]));
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "agent-a", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
let result2 = pack
.dispatch("brain.bindings", json!({}), ®istry, &token)
.await
.unwrap();
assert_eq!(result2["count"], json!(1u64));
let rows = result2["bindings"].as_array().unwrap();
assert_eq!(rows[0]["actor"], json!("agent-a"));
assert_eq!(rows[0]["profile_id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn w4_h2_bindings_filtered() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "agent-1", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "agent-2", "consumer_kind": "search"}),
®istry,
&token,
)
.await
.unwrap();
let result = pack
.dispatch(
"brain.bindings",
json!({"actor": "agent-1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["count"], json!(1u64));
assert_eq!(result["bindings"][0]["actor"], json!("agent-1"));
}
#[tokio::test]
async fn w4_h3_resolve_returns_both_requested_and_matched_kind() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "consumer_kind": "*", "priority": 1}),
®istry,
&token,
)
.await
.unwrap();
let result = pack
.dispatch(
"brain.resolve",
json!({"consumer_kind": "search"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
result["requested_consumer_kind"],
json!("search"),
"H3: requested_consumer_kind must equal the query"
);
assert_eq!(
result["matched_consumer_kind"],
json!("*"),
"H3: matched_consumer_kind must show the wildcard binding"
);
assert_eq!(result["resolved_profile_id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn w4_h3_resolve_exact_match_returns_exact_kind() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch(
"brain.resolve",
json!({"consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(result["requested_consumer_kind"], json!("recall"));
assert_eq!(result["matched_consumer_kind"], json!("recall"));
}
#[tokio::test]
async fn r2_archived_exact_binding_defers_to_live_wildcard() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.create_profile",
json!({"name": "search-v1", "consumer_kind": "search"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.activate",
json!({"profile_id": "search-v1"}),
®istry,
&token,
)
.await
.unwrap();
{
let mut state = pack.state.lock().unwrap();
state.bindings.push(khive_brain_core::ProfileBinding {
actor: "*".into(),
namespace: "*".into(),
consumer_kind: "search".into(),
profile_id: "balanced-recall-v1".into(),
priority: 100,
created_at: chrono::Utc::now(),
});
state
.profiles
.get_mut("balanced-recall-v1")
.unwrap()
.lifecycle = khive_brain_core::ProfileLifecycle::Archived;
}
pack.dispatch(
"brain.bind",
json!({"profile_id": "search-v1", "consumer_kind": "*", "priority": 1}),
®istry,
&token,
)
.await
.unwrap();
let result = pack
.dispatch(
"brain.resolve",
json!({"consumer_kind": "search"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
result["resolved_profile_id"],
json!("search-v1"),
"r2 fix 3: archived high-priority binding must not suppress the live wildcard binding"
);
}
#[tokio::test]
async fn r2_feedback_rejects_archived_served_by_profile() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let err = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"served_by_profile_id": "balanced-recall-v1"
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("archived"),
"r2 fix 4: feedback to archived profile must mention 'archived'; got: {msg}"
);
} else {
panic!("r2 fix 4: feedback to archived served_by_profile_id must return InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn r2_create_profile_rejects_empty_consumer_kind() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": "bad-profile", "consumer_kind": ""}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"r2 fix 5: empty consumer_kind must return InvalidInput, got {err:?}"
);
}
#[tokio::test]
async fn r2_create_profile_rejects_wildcard_consumer_kind() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": "wildcard-profile", "consumer_kind": "*"}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("wildcard") || msg.contains("sentinel") || msg.contains("*"),
"r2 fix 5: wildcard consumer_kind rejection must explain the issue; got: {msg}"
);
} else {
panic!("r2 fix 5: wildcard consumer_kind must return InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn r2_create_profile_rejects_whitespace_consumer_kind() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": "ws-profile", "consumer_kind": " "}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"r2 fix 5: whitespace consumer_kind must return InvalidInput, got {err:?}"
);
}
#[tokio::test]
async fn r2_bindings_and_semantics_multi_filter() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
for name in ["alpha-v1", "beta-v1"] {
pack.dispatch(
"brain.create_profile",
json!({"name": name, "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.activate",
json!({"profile_id": name}),
®istry,
&token,
)
.await
.unwrap();
}
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "agent-A", "namespace": "ns-1", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "alpha-v1", "actor": "agent-A", "namespace": "ns-2", "consumer_kind": "search"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.bind",
json!({"profile_id": "beta-v1", "actor": "agent-B", "namespace": "ns-1", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
let r1 = pack
.dispatch(
"brain.bindings",
json!({"profile_id": "balanced-recall-v1", "namespace": "ns-1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
r1["count"],
json!(1u64),
"AND filter profile_id+namespace must return 1 row"
);
assert_eq!(r1["bindings"][0]["profile_id"], json!("balanced-recall-v1"));
assert_eq!(r1["bindings"][0]["namespace"], json!("ns-1"));
let r2 = pack
.dispatch(
"brain.bindings",
json!({"actor": "agent-A", "consumer_kind": "search"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
r2["count"],
json!(1u64),
"AND filter actor+consumer_kind must return 1 row"
);
assert_eq!(r2["bindings"][0]["profile_id"], json!("alpha-v1"));
let r3 = pack
.dispatch(
"brain.bindings",
json!({"actor": "agent-B", "consumer_kind": "search"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(
r3["count"],
json!(0u64),
"AND filter with no matches must return count=0"
);
assert_eq!(r3["bindings"], json!([]));
}
#[tokio::test]
async fn r2_user_profile_reset_mutates_posteriors() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
pack.dispatch(
"brain.create_profile",
json!({"name": "custom-v1", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.activate",
json!({"profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful", "served_by_profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
let mutated = pack
.dispatch(
"brain.profile",
json!({"profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
let salience_alpha_before = mutated["state_snapshot"]["salience"]["alpha"]
.as_f64()
.expect("state_snapshot.salience.alpha() must be a number");
assert!(
salience_alpha_before > 2.0,
"r3 fix 2: feedback must have moved salience alpha above prior 2.0; got {salience_alpha_before}"
);
let epoch_before = mutated["exploration_epoch"].as_u64().unwrap();
let reset_result = pack
.dispatch(
"brain.reset",
json!({"profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(reset_result["reset"], json!(true));
assert_eq!(reset_result["profile_id"], json!("custom-v1"));
let after = pack
.dispatch(
"brain.profile",
json!({"profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
let epoch_after = after["exploration_epoch"].as_u64().unwrap();
assert!(
epoch_after > epoch_before,
"r3 fix 2: reset must increment exploration_epoch on user-created profile; before={epoch_before} after={epoch_after}"
);
let snap = &after["state_snapshot"];
assert!(
!snap.is_null(),
"r3 fix 2: state_snapshot must be non-null after reset"
);
let rel_alpha = snap["relevance"]["alpha"]
.as_f64()
.expect("relevance.alpha()");
let rel_beta = snap["relevance"]["beta"]
.as_f64()
.expect("relevance.beta()");
assert!(
(rel_alpha - 7.0).abs() < 1e-9 && (rel_beta - 3.0).abs() < 1e-9,
"r3 fix 2: relevance must be Beta(7,3) after reset; got ({rel_alpha},{rel_beta})"
);
let sal_alpha = snap["salience"]["alpha"]
.as_f64()
.expect("salience.alpha()");
let sal_beta = snap["salience"]["beta"].as_f64().expect("salience.beta()");
assert!(
(sal_alpha - 2.0).abs() < 1e-9 && (sal_beta - 8.0).abs() < 1e-9,
"r3 fix 2: salience must be Beta(2,8) after reset; got ({sal_alpha},{sal_beta})"
);
let tmp_alpha = snap["temporal"]["alpha"]
.as_f64()
.expect("temporal.alpha()");
let tmp_beta = snap["temporal"]["beta"].as_f64().expect("temporal.beta()");
assert!(
(tmp_alpha - 1.0).abs() < 1e-9 && (tmp_beta - 9.0).abs() < 1e-9,
"r3 fix 2: temporal must be Beta(1,9) after reset; got ({tmp_alpha},{tmp_beta})"
);
}
#[tokio::test]
async fn r2_user_profile_feedback_routes_to_profile_state() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
pack.dispatch(
"brain.create_profile",
json!({"name": "custom-v1", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.activate",
json!({"profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
let before = pack
.dispatch(
"brain.profile",
json!({"profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
let events_before = before["total_events"].as_u64().unwrap();
pack.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful", "served_by_profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
let after = pack
.dispatch(
"brain.profile",
json!({"profile_id": "custom-v1"}),
®istry,
&token,
)
.await
.unwrap();
let events_after = after["total_events"].as_u64().unwrap();
assert!(
events_after > events_before,
"r2 fix 2: feedback routed to custom profile must increment its total_events; before={events_before} after={events_after}"
);
}
#[tokio::test]
async fn w4_h4_profile_accepts_profile_id_and_id_alias() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let r1 = pack
.dispatch(
"brain.profile",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(r1["id"], json!("balanced-recall-v1"));
let r2 = pack
.dispatch(
"brain.profile",
json!({"id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
assert_eq!(r2["id"], json!("balanced-recall-v1"));
}
#[tokio::test]
async fn r3_feedback_default_profile_archived_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
pack.dispatch(
"brain.deactivate",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
pack.dispatch(
"brain.archive",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let snap_before = pack
.dispatch(
"brain.profile",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let events_before = snap_before["total_events"].as_u64().unwrap_or(0);
let log_before = pack
.dispatch("brain.events", json!({"limit": 1000}), ®istry, &token)
.await
.unwrap();
let log_count_before = log_before["events"]
.as_array()
.map(|a| a.len())
.unwrap_or(0);
let err = pack
.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful"}),
®istry,
&token,
)
.await
.unwrap_err();
match &err {
RuntimeError::InvalidInput(msg) => {
assert!(
msg.contains("archived"),
"r3-1: error must mention 'archived'; got: {msg}"
);
}
other => panic!("r3-1: expected InvalidInput(archived), got {other:?}"),
}
let snap_after = pack
.dispatch(
"brain.profile",
json!({"profile_id": "balanced-recall-v1"}),
®istry,
&token,
)
.await
.unwrap();
let events_after = snap_after["total_events"].as_u64().unwrap_or(0);
assert_eq!(
events_after, events_before,
"r3-1: archived default profile must not have events appended; before={events_before} after={events_after}"
);
let log_after = pack
.dispatch("brain.events", json!({"limit": 1000}), ®istry, &token)
.await
.unwrap();
let log_count_after = log_after["events"].as_array().map(|a| a.len()).unwrap_or(0);
assert_eq!(
log_count_after, log_count_before,
"r3-1: rejected feedback must not append a FeedbackExplicit event; before={log_count_before} after={log_count_after}"
);
}
#[tokio::test]
async fn r3_create_profile_id_grammar_enforced() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": " ", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"r3-3: whitespace-only name must return InvalidInput; got {err:?}"
);
pack.dispatch(
"brain.create_profile",
json!({"name": " my-profile ", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.expect("r3-3: name with leading/trailing spaces should be accepted after trim");
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": "bad.profile", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"r3-3: dot in name must return InvalidInput; got {err:?}"
);
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": "bad_profile", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"r3-3: underscore in name must return InvalidInput; got {err:?}"
);
let err = pack
.dispatch(
"brain.create_profile",
json!({"name": "*", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.unwrap_err();
assert!(
matches!(err, RuntimeError::InvalidInput(_)),
"r3-3: asterisk name must return InvalidInput; got {err:?}"
);
pack.dispatch(
"brain.create_profile",
json!({"name": "valid-profile-123", "consumer_kind": "recall"}),
®istry,
&token,
)
.await
.expect("r3-3: valid alphanumeric-hyphen name must succeed");
}
#[tokio::test]
async fn test_289_feedback_event_records_nonzero_duration() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let result = pack
.dispatch(
"brain.feedback",
json!({"target_id": target, "signal": "useful"}),
®istry,
&token,
)
.await
.unwrap();
let event_id = result["event_id"].as_str().unwrap().to_string();
let log = pack
.dispatch("brain.events", json!({"limit": 100}), ®istry, &token)
.await
.unwrap();
let event = log["events"]
.as_array()
.unwrap()
.iter()
.find(|e| e["id"].as_str() == Some(event_id.as_str()))
.expect("#289: feedback event must appear in brain.events");
assert!(
event["duration_us"].as_i64().unwrap() > 0,
"#289: feedback event duration_us must be non-zero, got {}",
event["duration_us"]
);
}
#[tokio::test]
async fn brain_auto_feedback_emits_implicit_positive_for_first_result() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let result = pack
.dispatch(
"brain.auto_feedback",
json!({
"query": "recall calibration target",
"results": [{ "id": target }]
}),
®istry,
&token,
)
.await
.expect("auto_feedback succeeds");
assert_eq!(result["emitted"], json!(true), "emitted must be true");
assert_eq!(
result["signal"],
json!("implicit_positive"),
"default signal must be implicit_positive"
);
let returned_target_id = result["target_id"].as_str().unwrap_or("");
assert_eq!(
returned_target_id.len(),
36,
"target_id in auto_feedback response must be full 36-char UUID"
);
assert_eq!(
returned_target_id, target,
"target_id must match the created entity"
);
assert_eq!(
pack.snapshot().balanced_recall.total_events,
1,
"auto_feedback must increment total_events"
);
}
#[tokio::test]
async fn brain_auto_feedback_empty_results_returns_no_emit() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch(
"brain.auto_feedback",
json!({
"query": "empty recall results",
"results": []
}),
®istry,
&token,
)
.await
.expect("auto_feedback with empty results succeeds");
assert_eq!(result["emitted"], json!(false));
assert_eq!(result["reason"], json!("no_results"));
}
#[tokio::test]
async fn brain_auto_feedback_accepts_short_note_id_prefix() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let prefix = &target[..8];
let result = pack
.dispatch(
"brain.auto_feedback",
json!({
"query": "prefix resolution test",
"results": [{ "id": prefix }]
}),
®istry,
&token,
)
.await
.expect("auto_feedback with 8-char prefix succeeds");
assert_eq!(result["emitted"], json!(true));
assert_eq!(result["target_id"].as_str().unwrap_or("").len(), 36);
}
#[tokio::test]
async fn namespace_isolation_state_does_not_leak() {
use core::convert::TryFrom;
use khive_runtime::Namespace;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = BrainPack::new(rt.clone());
let registry = empty_registry();
let ns_a = Namespace::try_from("ns-a").expect("namespace a");
let ns_b = Namespace::try_from("ns-b").expect("namespace b");
let token_a = rt.authorize(ns_a).expect("token a");
let token_b = rt.authorize(ns_b).expect("token b");
pack.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "alice-a",
"namespace": "ns-a",
"consumer_kind": "recall",
}),
®istry,
&token_a,
)
.await
.expect("bind in namespace A");
let bindings_b = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_b)
.await
.expect("bindings in namespace B");
assert_eq!(
bindings_b["count"],
json!(0u64),
"namespace B must see 0 bindings; got: {bindings_b}"
);
let profiles_b = pack
.dispatch("brain.profiles", json!({}), ®istry, &token_b)
.await
.expect("profiles in namespace B");
let count_b = profiles_b["count"].as_u64().unwrap_or(0);
let profiles_a = pack
.dispatch("brain.profiles", json!({}), ®istry, &token_a)
.await
.expect("profiles in namespace A");
let count_a = profiles_a["count"].as_u64().unwrap_or(0);
assert_eq!(
count_b, count_a,
"both namespaces must see only the built-in default profile"
);
}
#[cfg(test)]
mod help_tests {
use super::*;
use crate::handlers::BRAIN_HANDLERS;
fn find_handler(name: &str) -> &'static HandlerDef {
BRAIN_HANDLERS
.iter()
.find(|h| h.name == name)
.unwrap_or_else(|| panic!("handler {name:?} not found in BRAIN_HANDLERS"))
}
#[test]
fn brain_feedback_params_non_empty_and_has_target_and_signal() {
let h = find_handler("brain.feedback");
assert!(!h.params.is_empty(), "brain.feedback must have params");
assert!(
h.params.iter().any(|p| p.name == "target_id" && p.required),
"brain.feedback must have required target_id param"
);
assert!(
h.params.iter().any(|p| p.name == "signal" && p.required),
"brain.feedback must have required signal param"
);
assert!(
h.params.iter().any(|p| p.name == "served_by_profile_id"),
"brain.feedback must document served_by_profile_id"
);
}
#[test]
fn brain_auto_feedback_handler_is_declared() {
let h = find_handler("brain.auto_feedback");
assert!(
h.params.iter().any(|p| p.name == "query" && p.required),
"brain.auto_feedback must have required query param"
);
assert!(
h.params.iter().any(|p| p.name == "results" && p.required),
"brain.auto_feedback must have required results param"
);
}
#[test]
fn brain_profile_params_has_required_profile_id() {
let h = find_handler("brain.profile");
assert!(!h.params.is_empty(), "brain.profile must have params");
assert!(
h.params
.iter()
.any(|p| p.name == "profile_id" && p.required),
"brain.profile must have required profile_id param (H4 fix)"
);
}
#[test]
fn brain_profiles_params_has_lifecycle_filter() {
let h = find_handler("brain.profiles");
assert!(!h.params.is_empty(), "brain.profiles must have params");
assert!(
h.params.iter().any(|p| p.name == "lifecycle"),
"brain.profiles must document lifecycle filter param"
);
}
#[test]
fn brain_resolve_params_has_consumer_kind_required() {
let h = find_handler("brain.resolve");
assert!(!h.params.is_empty(), "brain.resolve must have params");
assert!(
h.params
.iter()
.any(|p| p.name == "consumer_kind" && p.required),
"brain.resolve must have required consumer_kind"
);
assert!(
h.params.iter().any(|p| p.name == "actor"),
"brain.resolve must document optional actor"
);
assert!(
h.params.iter().any(|p| p.name == "namespace"),
"brain.resolve must document optional namespace"
);
}
#[test]
fn brain_bind_params_has_required_profile_id_and_optionals() {
let h = find_handler("brain.bind");
assert!(!h.params.is_empty(), "brain.bind must have params");
assert!(
h.params
.iter()
.any(|p| p.name == "profile_id" && p.required),
"brain.bind must have required profile_id"
);
assert!(
h.params.iter().any(|p| p.name == "actor"),
"brain.bind must document actor"
);
assert!(
h.params.iter().any(|p| p.name == "namespace"),
"brain.bind must document namespace"
);
assert!(
h.params.iter().any(|p| p.name == "consumer_kind"),
"brain.bind must document consumer_kind"
);
assert!(
h.params.iter().any(|p| p.name == "priority"),
"brain.bind must document priority"
);
}
#[test]
fn brain_unbind_params_non_empty_all_optional() {
let h = find_handler("brain.unbind");
assert!(!h.params.is_empty(), "brain.unbind must have params");
assert!(
h.params.iter().all(|p| !p.required),
"brain.unbind params must all be optional (filter semantics)"
);
assert!(
h.params.iter().any(|p| p.name == "profile_id"),
"brain.unbind must document profile_id filter"
);
assert!(
h.params.iter().any(|p| p.name == "actor"),
"brain.unbind must document actor filter"
);
}
#[test]
fn brain_activate_deactivate_archive_each_have_profile_id() {
for verb in ["brain.activate", "brain.deactivate", "brain.archive"] {
let h = find_handler(verb);
assert!(!h.params.is_empty(), "{verb} must have params");
assert!(
h.params
.iter()
.any(|p| p.name == "profile_id" && p.required),
"{verb} must have required profile_id param"
);
}
}
#[test]
fn brain_reset_params_has_optional_profile_id() {
let h = find_handler("brain.reset");
assert!(!h.params.is_empty(), "brain.reset must have params");
assert!(
h.params
.iter()
.any(|p| p.name == "profile_id" && !p.required),
"brain.reset profile_id must be optional (defaults to balanced-recall-v1)"
);
}
#[test]
fn brain_config_params_has_parameter() {
let h = find_handler("brain.config");
assert!(
!h.params.is_empty(),
"brain.config must document the parameter arg"
);
assert!(
h.params
.iter()
.any(|p| p.name == "parameter" && !p.required),
"brain.config parameter must be optional"
);
}
#[test]
fn brain_events_params_has_limit() {
let h = find_handler("brain.events");
assert!(
!h.params.is_empty(),
"brain.events must document the limit arg"
);
assert!(
h.params.iter().any(|p| p.name == "limit" && !p.required),
"brain.events limit must be optional"
);
}
#[test]
fn brain_emit_params_non_empty_with_target_and_signal() {
let h = find_handler("brain.emit");
assert!(
!h.params.is_empty(),
"brain.emit must have params (mirrors brain.feedback)"
);
assert!(
h.params.iter().any(|p| p.name == "target_id" && p.required),
"brain.emit must have required target_id"
);
assert!(
h.params.iter().any(|p| p.name == "signal" && p.required),
"brain.emit must have required signal"
);
}
#[test]
fn brain_bindings_params_all_optional() {
let h = find_handler("brain.bindings");
assert!(
h.params.iter().all(|p| !p.required),
"brain.bindings: all params must be optional filter args"
);
assert!(
h.params.iter().any(|p| p.name == "profile_id"),
"brain.bindings must document profile_id filter"
);
assert!(
h.params.iter().any(|p| p.name == "consumer_kind"),
"brain.bindings must document consumer_kind filter"
);
}
#[test]
fn brain_create_profile_params_has_required_name() {
let h = find_handler("brain.create_profile");
assert!(
!h.params.is_empty(),
"brain.create_profile must have params"
);
assert!(
h.params.iter().any(|p| p.name == "name" && p.required),
"brain.create_profile must have required name param"
);
assert!(
h.params
.iter()
.any(|p| p.name == "consumer_kind" && !p.required),
"brain.create_profile consumer_kind must be optional"
);
}
fn make_brain_registry() -> (khive_runtime::VerbRegistry, KhiveRuntime) {
use khive_pack_kg::KgPack;
use khive_runtime::VerbRegistryBuilder;
let rt = KhiveRuntime::memory().expect("in-memory runtime for brain registry");
let mut builder = VerbRegistryBuilder::new();
builder.register(KgPack::new(rt.clone()));
builder.register(BrainPack::new(rt.clone()));
let registry = builder.build().expect("kg+brain registry builds");
(registry, rt)
}
#[tokio::test]
async fn r2_h1_bind_via_registry_preserves_namespace() {
use serde_json::json;
let (registry, _rt) = make_brain_registry();
let result = registry
.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "alice",
"namespace": "team-a",
"consumer_kind": "recall",
}),
)
.await
.expect("brain.bind must succeed");
assert_eq!(
result["namespace"],
json!("team-a"),
"brain.bind response must echo the caller-supplied namespace"
);
let bindings = registry
.dispatch(
"brain.bindings",
json!({
"profile_id": "balanced-recall-v1",
"namespace": "team-a",
}),
)
.await
.expect("brain.bindings must succeed");
assert_eq!(
bindings["count"],
json!(1u64),
"must find exactly one binding for namespace=team-a"
);
assert_eq!(
bindings["bindings"][0]["namespace"],
json!("team-a"),
"stored binding namespace must be team-a, not wildcard"
);
}
#[tokio::test]
async fn r2_h1_resolve_via_registry_uses_namespace() {
use serde_json::json;
let (registry, _rt) = make_brain_registry();
registry
.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "alice",
"namespace": "team-a",
"consumer_kind": "recall",
}),
)
.await
.expect("brain.bind team-a");
let resolved = registry
.dispatch(
"brain.resolve",
json!({
"actor": "alice",
"namespace": "team-a",
"consumer_kind": "recall",
}),
)
.await
.expect("brain.resolve must succeed for team-a");
assert_eq!(
resolved["resolved_profile_id"],
json!("balanced-recall-v1"),
"resolve must return the profile bound for team-a"
);
}
#[tokio::test]
async fn r2_h1_unbind_via_registry_uses_namespace() {
use serde_json::json;
let (registry, _rt) = make_brain_registry();
registry
.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "alice",
"namespace": "team-a",
"consumer_kind": "recall",
}),
)
.await
.expect("bind team-a");
registry
.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "alice",
"namespace": "team-b",
"consumer_kind": "recall",
}),
)
.await
.expect("bind team-b");
let unbound = registry
.dispatch(
"brain.unbind",
json!({
"actor": "alice",
"namespace": "team-a",
}),
)
.await
.expect("unbind team-a");
assert_eq!(
unbound["unbound"],
json!(1u64),
"must remove exactly one binding (team-a)"
);
let remaining = registry
.dispatch(
"brain.bindings",
json!({
"actor": "alice",
"namespace": "team-b",
}),
)
.await
.expect("bindings after unbind");
assert_eq!(
remaining["count"],
json!(1u64),
"team-b binding must survive the team-a unbind"
);
}
#[tokio::test]
async fn profiles_output_is_sorted_by_id() {
use khive_runtime::{Namespace, PackRuntime, VerbRegistryBuilder};
use serde_json::json;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = BrainPack::new(rt.clone());
let registry = VerbRegistryBuilder::new().build().expect("empty registry");
let token = rt.authorize(Namespace::local()).unwrap();
pack.dispatch(
"brain.create_profile",
json!({ "name": "z-profile" }),
®istry,
&token,
)
.await
.expect("create z-profile");
pack.dispatch(
"brain.create_profile",
json!({ "name": "a-profile" }),
®istry,
&token,
)
.await
.expect("create a-profile");
let result = pack
.dispatch("brain.profiles", json!({}), ®istry, &token)
.await
.expect("profiles list");
let profiles = result["profiles"].as_array().expect("profiles array");
let ids: Vec<&str> = profiles.iter().filter_map(|p| p["id"].as_str()).collect();
let mut sorted = ids.clone();
sorted.sort();
assert_eq!(
ids, sorted,
"brain.profiles must return profiles sorted by id"
);
}
}
#[tokio::test]
async fn crit1_create_profile_rejects_seed_priors_exceeding_ess_cap() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.create_profile",
json!({
"name": "bad-ess-profile",
"consumer_kind": "search",
"seed_priors": {
"section_posteriors": {
"operational_guidance": {"alpha": 500.0, "beta": 500.0}
}
}
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("ESS") || msg.contains("alpha+beta") || msg.contains("exceeds"),
"error must mention the ESS constraint; got: {msg}"
);
} else {
panic!("expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn crit1_create_profile_accepts_seed_priors_within_ess_cap() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let result = pack
.dispatch(
"brain.create_profile",
json!({
"name": "ok-ess-profile",
"consumer_kind": "search",
"seed_priors": {
"section_posteriors": {
"operational_guidance": {"alpha": 6.0, "beta": 1.5}
}
}
}),
®istry,
&token,
)
.await
.expect("create with valid seed priors must succeed");
assert_eq!(result["created"], json!(true));
}
#[tokio::test]
async fn ensure_loaded_publication_is_atomic() {
use core::convert::TryFrom;
use khive_runtime::Namespace;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = BrainPack::new(rt.clone());
let registry = empty_registry();
let ns_a = Namespace::try_from("atomic-ns-a").expect("namespace a");
let ns_b = Namespace::try_from("atomic-ns-b").expect("namespace b");
let token_a = rt.authorize(ns_a).expect("token a");
let token_b = rt.authorize(ns_b).expect("token b");
pack.ensure_loaded(&token_a)
.await
.expect("ensure_loaded ns-a");
{
let t = pack.persistence.lock().unwrap();
assert_eq!(
t.active_namespace.as_deref(),
Some("atomic-ns-a"),
"active_namespace must equal requested namespace immediately after ensure_loaded"
);
assert!(
t.loaded_namespaces.contains_key("atomic-ns-a"),
"loaded_namespaces must contain the namespace immediately after ensure_loaded"
);
}
{
let s = pack.state.lock().unwrap();
assert!(
!s.profiles.is_empty(),
"shared state must contain at least the built-in profile after loading ns-a"
);
}
pack.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "actor-a",
"namespace": "atomic-ns-a",
"consumer_kind": "recall",
}),
®istry,
&token_a,
)
.await
.expect("bind in namespace A");
pack.ensure_loaded(&token_b)
.await
.expect("ensure_loaded ns-b");
{
let t = pack.persistence.lock().unwrap();
assert_eq!(
t.active_namespace.as_deref(),
Some("atomic-ns-b"),
"active_namespace must equal namespace B after switching to B"
);
assert!(
t.loaded_namespaces.contains_key("atomic-ns-b"),
"loaded_namespaces must contain ns-b after ensure_loaded"
);
assert!(
t.loaded_namespaces.contains_key("atomic-ns-a"),
"loaded_namespaces must still contain ns-a (saved, not evicted)"
);
}
let bindings_b = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_b)
.await
.expect("bindings in B");
assert_eq!(
bindings_b["count"],
json!(0u64),
"namespace B must see 0 bindings; the binding created in A must not bleed through"
);
pack.ensure_loaded(&token_a)
.await
.expect("ensure_loaded ns-a again");
{
let t = pack.persistence.lock().unwrap();
assert_eq!(
t.active_namespace.as_deref(),
Some("atomic-ns-a"),
"active_namespace must be restored to A"
);
}
let bindings_a = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_a)
.await
.expect("bindings in A after restore");
assert_eq!(
bindings_a["count"],
json!(1u64),
"binding created in namespace A must survive the save-restore round-trip"
);
}
#[tokio::test]
async fn ensure_loaded_concurrent_same_namespace_is_safe() {
use core::convert::TryFrom;
use khive_runtime::Namespace;
use std::sync::Arc;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = Arc::new(BrainPack::new(rt.clone()));
let registry = empty_registry();
let ns = Namespace::try_from("conc-ns").expect("conc namespace");
let token = rt.authorize(ns).expect("conc token");
let token = Arc::new(token);
let mut handles = Vec::new();
for _ in 0..8 {
let pack2 = Arc::clone(&pack);
let tok2 = Arc::clone(&token);
handles.push(tokio::spawn(
async move { pack2.ensure_loaded(&tok2).await },
));
}
for h in handles {
h.await
.expect("task did not panic")
.expect("ensure_loaded must not error");
}
{
let t = pack.persistence.lock().unwrap();
assert_eq!(
t.active_namespace.as_deref(),
Some("conc-ns"),
"active_namespace must be conc-ns after concurrent loads"
);
assert!(
t.loaded_namespaces.contains_key("conc-ns"),
"loaded_namespaces must contain conc-ns"
);
}
{
let s = pack.state.lock().unwrap();
assert!(
!s.profiles.is_empty(),
"shared state must be non-empty (built-in profile present)"
);
}
let result = pack
.dispatch("brain.profiles", json!({}), ®istry, &token)
.await
.expect("brain.profiles after concurrent ensure_loaded");
assert!(
result["count"].as_u64().unwrap_or(0) >= 1,
"at least the built-in profile must be present"
);
}
#[tokio::test]
async fn ensure_loaded_cross_namespace_concurrent_does_not_corrupt_saved_states() {
use core::convert::TryFrom;
use khive_runtime::Namespace;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = std::sync::Arc::new(BrainPack::new(rt.clone()));
let registry = empty_registry();
let ns_x = Namespace::try_from("xns-x").expect("x");
let ns_y = Namespace::try_from("xns-y").expect("y");
let token_x = rt.authorize(ns_x).expect("token x");
let token_y = rt.authorize(ns_y).expect("token y");
let token_x = std::sync::Arc::new(token_x);
let token_y = std::sync::Arc::new(token_y);
pack.ensure_loaded(&token_x).await.expect("load x");
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "x-actor", "namespace": "xns-x", "consumer_kind": "recall"}),
®istry,
&token_x,
)
.await
.expect("bind x");
pack.ensure_loaded(&token_y).await.expect("load y");
pack.dispatch(
"brain.bind",
json!({"profile_id": "balanced-recall-v1", "actor": "y-actor", "namespace": "xns-y", "consumer_kind": "recall"}),
®istry,
&token_y,
)
.await
.expect("bind y");
pack.ensure_loaded(&token_x).await.expect("reload x");
let bx = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_x)
.await
.expect("bindings x");
assert_eq!(
bx["count"],
json!(1u64),
"namespace X must still have exactly 1 binding after cross-namespace interleave"
);
pack.ensure_loaded(&token_y).await.expect("reload y");
let by = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_y)
.await
.expect("bindings y");
assert_eq!(
by["count"],
json!(1u64),
"namespace Y must still have exactly 1 binding after cross-namespace interleave"
);
}
#[tokio::test]
async fn concurrent_cold_load_does_not_clobber_live_state() {
use core::convert::TryFrom;
use khive_runtime::Namespace;
use std::sync::Arc;
use tokio::sync::oneshot;
struct HookGuard;
impl Drop for HookGuard {
fn drop(&mut self) {
persist::clear_post_load_hook();
}
}
let _guard = HookGuard;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = Arc::new(BrainPack::new(rt.clone()));
let registry = empty_registry();
let ns = Namespace::try_from("race-ns-det").expect("race namespace");
let token = Arc::new(rt.authorize(ns).expect("race token"));
let (reached_tx, reached_rx) = oneshot::channel::<()>();
let (proceed_tx, proceed_rx) = oneshot::channel::<()>();
persist::set_post_load_hook(persist::LoadHook {
reached_tx,
proceed_rx,
});
let pack_b = Arc::clone(&pack);
let token_b = Arc::clone(&token);
let b_handle = tokio::spawn(async move { pack_b.ensure_loaded(&token_b).await });
reached_rx.await.expect("loader B must signal reached");
pack.ensure_loaded(&token)
.await
.expect("loader A: ensure_loaded");
pack.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "racer",
"namespace": "race-ns-det",
"consumer_kind": "recall",
}),
®istry,
&token,
)
.await
.expect("brain.bind after A loaded");
let before = pack
.dispatch("brain.bindings", json!({}), ®istry, &token)
.await
.expect("bindings before B resumes");
assert_eq!(
before["count"],
json!(1u64),
"binding must exist before B resumes"
);
proceed_tx.send(()).expect("send proceed to B");
b_handle
.await
.expect("loader B task must not panic")
.expect("loader B ensure_loaded must not error");
let after = pack
.dispatch("brain.bindings", json!({}), ®istry, &token)
.await
.expect("bindings after B resumes");
assert_eq!(
after["count"],
json!(1u64),
"concurrent cold-load race: Loader B must not clobber the binding \
created by Loader A (old code would return 0 here)"
);
}
#[tokio::test]
async fn dispatch_gate_race_is_observable_without_gate() {
use core::convert::TryFrom;
use khive_runtime::Namespace;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = BrainPack::new(rt.clone());
let registry = empty_registry();
let ns_a = Namespace::try_from("bare-ns-a").expect("ns-a");
let ns_b = Namespace::try_from("bare-ns-b").expect("ns-b");
let token_a = rt.authorize(ns_a).expect("token a");
let token_b = rt.authorize(ns_b).expect("token b");
pack.ensure_loaded(&token_a).await.expect("ensure_loaded a");
pack.ensure_loaded(&token_b).await.expect("ensure_loaded b");
pack.handle_bind(json!({
"profile_id": "balanced-recall-v1",
"actor": "racer-a",
"namespace": "bare-ns-a",
"consumer_kind": "recall",
}))
.await
.expect("handle_bind for a");
let bindings_b_now = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_b)
.await
.expect("bindings ns-b");
assert_eq!(
bindings_b_now["count"],
json!(1u64),
"race reproduced: A's bind wrote into the ns-b slot (WRONG namespace)"
);
let bindings_a_now = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_a)
.await
.expect("bindings ns-a");
assert_eq!(
bindings_a_now["count"],
json!(0u64),
"race reproduced: ns-a has 0 bindings because A's write went to ns-b"
);
}
#[tokio::test]
async fn dispatch_gate_prevents_cross_namespace_slot_swap() {
use core::convert::TryFrom;
use khive_runtime::Namespace;
use std::sync::Arc;
use tokio::sync::oneshot;
struct HookGuard;
impl Drop for HookGuard {
fn drop(&mut self) {
crate::pack::clear_dispatch_interleave_hook();
}
}
let _guard = HookGuard;
let rt = KhiveRuntime::memory().expect("in-memory runtime");
let pack = Arc::new(BrainPack::new(rt.clone()));
let registry = Arc::new(empty_registry());
let ns_a = Namespace::try_from("gate-ns-a").expect("ns-a");
let ns_b = Namespace::try_from("gate-ns-b").expect("ns-b");
let token_a = Arc::new(rt.authorize(ns_a).expect("token a"));
let token_b = Arc::new(rt.authorize(ns_b).expect("token b"));
let (reached_tx, reached_rx) = oneshot::channel::<()>();
let (proceed_tx, proceed_rx) = oneshot::channel::<()>();
crate::pack::set_dispatch_interleave_hook(crate::pack::DispatchHook {
reached_tx,
proceed_rx,
});
let pack_a = Arc::clone(&pack);
let token_a2 = Arc::clone(&token_a);
let registry_a = Arc::clone(®istry);
let a_handle = tokio::spawn(async move {
pack_a
.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "racer-a",
"namespace": "gate-ns-a",
"consumer_kind": "recall",
}),
®istry_a,
&token_a2,
)
.await
});
reached_rx.await.expect("dispatch A must reach hook");
let pack_b = Arc::clone(&pack);
let token_b2 = Arc::clone(&token_b);
let registry_b = Arc::clone(®istry);
let b_handle = tokio::spawn(async move {
pack_b
.dispatch(
"brain.bind",
json!({
"profile_id": "balanced-recall-v1",
"actor": "racer-b",
"namespace": "gate-ns-b",
"consumer_kind": "recall",
}),
®istry_b,
&token_b2,
)
.await
});
proceed_tx.send(()).expect("send proceed to A");
a_handle
.await
.expect("dispatch A task must not panic")
.expect("dispatch A must not error");
b_handle
.await
.expect("dispatch B task must not panic")
.expect("dispatch B must not error");
let bindings_a = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_a)
.await
.expect("bindings for ns-a");
assert_eq!(
bindings_a["count"],
json!(1u64),
"gate: ns-a must have exactly 1 binding (racer-a)"
);
let bindings_b = pack
.dispatch("brain.bindings", json!({}), ®istry, &token_b)
.await
.expect("bindings for ns-b");
assert_eq!(
bindings_b["count"],
json!(1u64),
"gate: ns-b must have exactly 1 binding (racer-b)"
);
}
#[cfg(test)]
mod brain_005_section_signals {
use super::*;
#[tokio::test]
async fn section_signals_not_an_object_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let err = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": ["overview", "useful"]
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("section_signals"),
"error must mention section_signals; got: {msg}"
);
} else {
panic!("expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn section_signals_unknown_section_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let err = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": {"not_a_real_section": "useful"}
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("not_a_real_section"),
"error must name the bad key; got: {msg}"
);
assert!(
msg.contains("valid") || msg.contains("overview"),
"error must list valid sections; got: {msg}"
);
} else {
panic!("expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn section_signals_unknown_signal_value_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let err = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": {"overview": "garbage_signal"}
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("garbage_signal"),
"error must name the bad signal; got: {msg}"
);
} else {
panic!("expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn section_signals_non_string_value_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let err = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": {"overview": 42}
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("section_signals"),
"error must mention section_signals; got: {msg}"
);
assert!(
msg.contains("useful") || msg.contains("not_useful"),
"error for non-string signal must name valid values; got: {msg}"
);
} else {
panic!("expected InvalidInput, got {err:?}");
}
}
#[tokio::test]
async fn section_signals_valid_object_is_accepted() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let result = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": {
"overview": "useful",
"formalism": "not_useful"
}
}),
®istry,
&token,
)
.await
.expect("valid section_signals must be accepted");
assert_eq!(result["emitted"], json!(true));
}
#[tokio::test]
async fn section_signals_empty_map_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
let err = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": {}
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("section_signals"),
"error must mention section_signals; got: {msg}"
);
} else {
panic!("expected InvalidInput for empty section_signals, got {err:?}");
}
}
#[tokio::test]
async fn section_signals_semantic_signal_value_is_rejected() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
for semantic_value in [
"explicit_positive",
"explicit_negative",
"implicit_positive",
"implicit_negative",
"correction",
] {
let err = pack
.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": {"overview": semantic_value}
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains(semantic_value),
"error must name the invalid signal {semantic_value:?}; got: {msg}"
);
assert!(
msg.contains("useful") || msg.contains("not_useful"),
"error must list valid section signals; got: {msg}"
);
} else {
panic!(
"expected InvalidInput for semantic section signal {semantic_value:?}, got {err:?}"
);
}
}
}
}
#[tokio::test]
async fn feedback_accepts_short_prefix_target_id() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let full_uuid = create_test_entity(&rt, &token).await;
let prefix = &full_uuid[..8];
let result = pack
.dispatch(
"brain.feedback",
json!({
"target_id": prefix,
"signal": "useful",
"served_by_profile_id": "balanced-recall-v1"
}),
®istry,
&token,
)
.await
.expect("brain.feedback must accept an 8-char hex prefix for target_id");
assert_eq!(result["emitted"], json!(true), "emitted must be true");
assert_eq!(result["signal"], json!("useful"), "signal must round-trip");
}
#[tokio::test]
async fn register_adapter_accept_matching_revision() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let active_rev = std::env::var("KHIVE_BRAIN_BASE_MODEL_REVISION")
.unwrap_or_else(|_| crate::handlers::DEFAULT_BASE_MODEL_REVISION.to_string());
let result = pack
.dispatch(
"brain.register_adapter",
json!({
"adapter_id": "lora-v1",
"content_hash": "sha256:abcd1234",
"base_model_revision": active_rev,
}),
®istry,
&token,
)
.await
.expect("register_adapter with matching revision must succeed");
assert_eq!(result["registered"], json!(true), "registered must be true");
assert_eq!(result["adapter_id"], json!("lora-v1"));
assert_eq!(result["content_hash"], json!("sha256:abcd1234"));
assert_eq!(result["base_model_revision"], json!(active_rev));
let entities = rt
.list_entities(&token, Some("artifact"), Some("adapter"), 10, 0)
.await
.expect("list_entities must succeed");
let found = entities.iter().any(|e| e.name == "lora-v1");
assert!(
found,
"artifact entity 'lora-v1' must exist after successful registration"
);
let entity = entities.iter().find(|e| e.name == "lora-v1").unwrap();
let props = entity
.properties
.as_ref()
.expect("entity must have properties");
assert_eq!(
props["content_hash"],
json!("sha256:abcd1234"),
"content_hash must be stored in entity properties"
);
}
#[tokio::test]
async fn register_adapter_reject_mismatching_revision() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let err = pack
.dispatch(
"brain.register_adapter",
json!({
"adapter_id": "lora-bad",
"content_hash": "sha256:deadbeef",
"base_model_revision": "wrong-revision-xyz",
}),
®istry,
&token,
)
.await
.unwrap_err();
if let RuntimeError::InvalidInput(msg) = &err {
assert!(
msg.contains("mismatch") || msg.contains("expected"),
"#354: rejection message must name the mismatch; got: {msg}"
);
assert!(
msg.contains("wrong-revision-xyz") || msg.contains("base_model_revision"),
"#354: rejection message must name the supplied revision; got: {msg}"
);
} else {
panic!("#354: mismatching revision must return InvalidInput, got {err:?}");
}
let entities = rt
.list_entities(&token, Some("artifact"), Some("adapter"), 10, 0)
.await
.expect("list_entities must succeed");
assert!(
!entities.iter().any(|e| e.name == "lora-bad"),
"#354: rejected registration must not persist an artifact entity"
);
}
#[cfg(feature = "lattice-router")]
mod router_section_tests {
use super::*;
use khive_brain_core::{SectionPosteriorState, SectionType};
#[tokio::test]
async fn feedback_section_signals_seeds_balanced_recall_section_state() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
{
let state = pack.state.lock().unwrap();
assert!(
!state.section_states.contains_key("balanced-recall-v1"),
"section_states must not contain balanced-recall-v1 before first feedback"
);
}
pack.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"section_signals": {
"operational_guidance": "useful"
}
}),
®istry,
&token,
)
.await
.expect("feedback must succeed");
let state = pack.state.lock().unwrap();
let ss = state
.section_states
.get("balanced-recall-v1")
.expect("balanced-recall-v1 must have a section_states entry after first feedback");
assert_eq!(
ss.posteriors.len(),
SectionType::all().len(),
"section state must contain all {} SectionType slots after seeding",
SectionType::all().len()
);
let default_og_mean = SectionPosteriorState::default_priors()
.get(&SectionType::OperationalGuidance)
.expect("default_priors must include OperationalGuidance")
.mean();
let live_og_mean = ss
.posteriors
.get(&SectionType::OperationalGuidance)
.expect("OperationalGuidance must be present after seeding")
.mean();
assert!(
live_og_mean > default_og_mean,
"OperationalGuidance mean must increase after useful signal; \
default={default_og_mean:.4} live={live_og_mean:.4}"
);
}
#[tokio::test]
async fn feedback_section_signals_updates_custom_profile_with_seeded_priors() {
let (pack, rt) = make_pack();
let registry = empty_registry();
let token = rt.authorize(Namespace::local()).unwrap();
let target = create_test_entity(&rt, &token).await;
pack.dispatch(
"brain.create_profile",
json!({
"name": "test-section-custom",
"description": "custom profile for router section-posterior regression",
"consumer_kind": "recall",
"seed_priors": {
"section_posteriors": {
"formalism": {"alpha": 8.0, "beta": 2.0}
}
}
}),
®istry,
&token,
)
.await
.expect("brain.create_profile must succeed");
let formalism_seeded_mean = {
let state = pack.state.lock().unwrap();
state
.section_states
.get("test-section-custom")
.expect("test-section-custom must have section_states after create_profile")
.posteriors
.get(&SectionType::Formalism)
.expect("Formalism must be present in seeded state")
.mean()
};
pack.dispatch(
"brain.feedback",
json!({
"target_id": target,
"signal": "useful",
"served_by_profile_id": "test-section-custom",
"section_signals": {
"formalism": "useful"
}
}),
®istry,
&token,
)
.await
.expect("feedback to custom profile must succeed");
let formalism_live_mean = {
let state = pack.state.lock().unwrap();
state
.section_states
.get("test-section-custom")
.expect("test-section-custom must still have section_states after feedback")
.posteriors
.get(&SectionType::Formalism)
.expect("Formalism must still be present after feedback")
.mean()
};
assert!(
formalism_live_mean > formalism_seeded_mean,
"Formalism posterior mean must increase after useful signal; \
seeded={formalism_seeded_mean:.4} live={formalism_live_mean:.4}"
);
}
#[tokio::test]
async fn replay_seeds_section_posteriors_for_missing_profile() {
use khive_brain_core::BrainState;
use khive_storage::event::Event as StorageEvent;
use khive_types::{EventKind, SubstrateKind};
use uuid::Uuid;
let rt = khive_runtime::KhiveRuntime::memory().expect("in-memory runtime");
let token = rt.authorize(khive_runtime::Namespace::local()).unwrap();
let namespace = token.namespace().as_str();
let snapshot = BrainState::new(crate::ENTITY_CACHE_CAPACITY).to_snapshot();
let t0_us: i64 = 1_000;
crate::persist::upsert_snapshot(rt.sql().as_ref(), namespace, &snapshot, t0_us)
.await
.expect("upsert snapshot");
let mut ev = StorageEvent::new(
namespace,
"brain.feedback",
EventKind::Audit,
SubstrateKind::Event,
"brain",
);
ev.target_id = Some(Uuid::new_v4());
ev.payload = serde_json::json!({
"signal": "useful",
"section_signals": {"operational_guidance": "useful"},
});
let event_value = serde_json::to_value(&ev).expect("serialize event");
let t1_us: i64 = t0_us + 1_000;
crate::persist::append_brain_event(
rt.sql().as_ref(),
namespace,
"balanced-recall-v1",
"brain.feedback",
&event_value,
t1_us,
)
.await
.expect("append brain event");
let pack2 = crate::BrainPack::new(rt.clone());
let registry = empty_registry();
pack2
.dispatch("brain.profiles", serde_json::json!({}), ®istry, &token)
.await
.expect("brain.profiles dispatch after replay");
let state = pack2.state.lock().unwrap();
let ss = state
.section_states
.get("balanced-recall-v1")
.expect("balanced-recall-v1 must have a section_states entry after replay seeding");
assert_eq!(
ss.posteriors.len(),
SectionType::all().len(),
"replay must seed all {} SectionType slots; got {}",
SectionType::all().len(),
ss.posteriors.len()
);
let default_og_mean = SectionPosteriorState::default_priors()
.get(&SectionType::OperationalGuidance)
.expect("default_priors must include OperationalGuidance")
.mean();
let replayed_og_mean = ss
.posteriors
.get(&SectionType::OperationalGuidance)
.expect("OperationalGuidance must be present after replay seeding")
.mean();
assert!(
replayed_og_mean > default_og_mean,
"OperationalGuidance mean must increase after useful replay signal; \
default={default_og_mean:.4} replayed={replayed_og_mean:.4}"
);
}
}