khive-runtime 0.10.0

Composable Service API: entity/note CRUD, graph traversal, hybrid search, curation.
Documentation
async fn plan_raw_round_trip(payload: serde_json::Value) -> (DaemonResponseFrame, usize) {
    let calls = Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let dispatcher = MockDispatch {
        namespace: "local".into(),
        config_id: "plan-config".into(),
        dispatch_calls: Arc::clone(&calls),
        pool: None,
        dispatch_err: None,
    };
    let (mut client, server) = UnixStream::pair().unwrap();
    let handle = tokio::spawn(async move {
        handle_conn(server, dispatcher).await;
    });
    write_frame(&mut client, &serde_json::to_vec(&payload).unwrap())
        .await
        .unwrap();
    let raw = read_frame(&mut client).await.unwrap();
    handle.await.unwrap();
    (
        serde_json::from_slice(&raw).unwrap(),
        calls.load(std::sync::atomic::Ordering::SeqCst),
    )
}

#[tokio::test]
async fn plan_daemon_frame_returns_plan_without_dispatch() {
    let payload = serde_json::json!({
        "ops":"missing_verb()", "namespace":"", "plan":true,
        "config_id":"plan-config", "protocol_version":PROTOCOL_VERSION
    });
    let (response, calls) = plan_raw_round_trip(payload).await;
    assert!(response.ok);
    assert_eq!(calls, 0);
    let result: serde_json::Value =
        serde_json::from_str(response.result.as_deref().unwrap()).unwrap();
    assert_eq!(result["parsed"], true);
    assert_eq!(result["stages"][0]["known"], false);
    assert!(response.request_id.is_none());
}

#[tokio::test]
async fn plan_daemon_refuses_each_present_companion_including_null() {
    for (field, value) in [
        ("presentation", serde_json::json!("verbose")),
        ("presentation_per_op", serde_json::json!([null])),
        ("format", serde_json::json!("json")),
        ("format_per_op", serde_json::json!([null])),
        ("request_id", serde_json::json!(7)),
    ] {
        for value in [
            value,
            serde_json::Value::Null,
            serde_json::json!({"invalid":"type"}),
        ] {
            let mut payload = serde_json::json!({
                "ops":"missing_verb()", "namespace":"", "plan":true,
                "config_id":"plan-config", "protocol_version":PROTOCOL_VERSION
            });
            payload[field] = value;
            let (response, calls) = plan_raw_round_trip(payload).await;
            assert!(!response.ok, "{field}");
            assert_eq!(calls, 0, "{field}");
            assert_eq!(
                response.error_detail.as_ref().unwrap()["domain_disposition"],
                "not_committed"
            );
            let error = response.error.unwrap();
            assert!(
                error.contains("invalid_params") && error.contains(field),
                "{error}"
            );
        }
    }
}

#[tokio::test]
async fn plan_daemon_protocol_rejects_previous_version_without_dispatch() {
    assert_eq!(PROTOCOL_VERSION, 8);
    let (response, calls) = plan_raw_round_trip(serde_json::json!({
        "ops":"missing_verb()", "namespace":"", "plan":true,
        "config_id":"plan-config", "protocol_version":4
    }))
    .await;
    assert!(!response.ok);
    assert!(
        !response.version_mismatch,
        "a client below this daemon's protocol is refused with the flag clear: it is \
         reserved for a client that is ahead, and deployed bridges recover on this shape"
    );
    assert_eq!(
        response.error_detail.as_ref().unwrap()["code"],
        "version_mismatch",
        "the refusal stays typed even though the flag is clear"
    );
    assert_eq!(calls, 0);
}

#[tokio::test]
async fn plan_daemon_duplicate_flags_do_not_reach_dispatch() {
    let calls = Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let dispatcher = MockDispatch {
        namespace: "local".into(),
        config_id: "plan-config".into(),
        dispatch_calls: Arc::clone(&calls),
        pool: None,
        dispatch_err: None,
    };
    let (mut client, peer) = UnixStream::pair().unwrap();
    let task = tokio::spawn(async move {
        handle_conn(peer, dispatcher).await;
    });
    let payload = format!(
        r#"{{"ops":"missing_verb()","namespace":"","config_id":"plan-config","protocol_version":{PROTOCOL_VERSION},"plan":true,"plan":false}}"#
    );
    write_frame(&mut client, payload.as_bytes()).await.unwrap();
    let response = tokio::time::timeout(std::time::Duration::from_secs(5), read_frame(&mut client))
        .await
        .unwrap();
    assert!(response.is_err(), "duplicate fields must fail decoding");
    task.await.unwrap();
    assert_eq!(calls.load(std::sync::atomic::Ordering::SeqCst), 0);
}