Skip to main content

public_06_handoff_feedback/
06_handoff_feedback.rs

1#[path = "../support/utils.rs"]
2mod _utils;
3
4use _utils::{boxed_error, cleanup_run, create_client, new_run_id, print_summary, require};
5use mubit_sdk::{
6    FeedbackOptions, GetContextOptions, HandoffOptions, RegisterAgentOptions, TransportMode,
7};
8use serde_json::json;
9use std::error::Error;
10use std::time::Instant;
11
12#[tokio::main(flavor = "current_thread")]
13async fn main() -> Result<(), Box<dyn Error>> {
14    let name = "public_06_handoff_feedback";
15    let started = Instant::now();
16    let client = create_client().await?;
17    let run_id = new_run_id("public_06_handoff_feedback");
18    client.set_run_id(Some(run_id.clone()));
19    client.set_transport(TransportMode::Http);
20
21    let mut passed = true;
22    let mut detail = "validated helper handoff + feedback flow".to_string();
23    let mut metrics = json!({});
24
25    let scenario = async {
26        let mut planner = RegisterAgentOptions::new("planner");
27        planner.run_id = Some(run_id.clone());
28        planner.role = "planner".to_string();
29        client.register_agent(planner).await?;
30
31        let mut reviewer = RegisterAgentOptions::new("reviewer");
32        reviewer.run_id = Some(run_id.clone());
33        reviewer.role = "reviewer".to_string();
34        client.register_agent(reviewer).await?;
35
36        let mut handoff = HandoffOptions::new(
37            "task-1",
38            "planner",
39            "reviewer",
40            "Review the retry policy patch before rollout.",
41        );
42        handoff.run_id = Some(run_id.clone());
43        handoff.requested_action = "review".to_string();
44        handoff.metadata = Some(json!({ "source": "rust-helper-example" }));
45        let handoff_response = client.handoff(handoff).await?;
46        let handoff_id = handoff_response
47            .get("handoff_id")
48            .and_then(|value| value.as_str())
49            .ok_or_else(|| boxed_error(format!("handoff id missing: {handoff_response}")))?
50            .to_string();
51
52        let mut feedback = FeedbackOptions::new(handoff_id.clone(), "approve");
53        feedback.run_id = Some(run_id.clone());
54        feedback.comments = "Patch looks safe for rollout.".to_string();
55        feedback.from_agent_id = Some("reviewer".to_string());
56        feedback.metadata = Some(json!({ "source": "rust-helper-example" }));
57        let feedback_response = client.feedback(feedback).await?;
58
59        let mut context = GetContextOptions::default();
60        context.run_id = Some(run_id.clone());
61        context.query = Some("What coordination artifacts were recorded for this review handoff?".to_string());
62        context.mode = Some("sections".to_string());
63        context.sections = vec!["handoffs".to_string(), "feedback".to_string()];
64        let context_response = client.get_context(context).await?;
65
66        require(
67            handoff_response
68                .get("success")
69                .and_then(|value| value.as_bool())
70                .unwrap_or(false),
71            format!("handoff failed: {handoff_response}"),
72        )?;
73        require(
74            feedback_response
75                .get("success")
76                .and_then(|value| value.as_bool())
77                .unwrap_or(false),
78            format!("feedback failed: {feedback_response}"),
79        )?;
80        let section_count = context_response
81            .get("section_summaries")
82            .and_then(|value| value.as_array())
83            .map(|items| items.len())
84            .unwrap_or(0);
85
86        metrics = json!({
87            "run_id": run_id,
88            "handoff_id": handoff_id,
89            "feedback_id": feedback_response.get("feedback_id").cloned().unwrap_or(serde_json::Value::Null),
90            "section_count": section_count,
91        });
92
93        Ok::<(), Box<dyn Error>>(())
94    }
95    .await;
96
97    if let Err(err) = scenario {
98        passed = false;
99        detail = err.to_string();
100    }
101
102    let cleanup_ok = cleanup_run(&client, &run_id).await;
103    if !cleanup_ok {
104        passed = false;
105        detail = format!("{detail} | cleanup failures");
106    }
107
108    print_summary(
109        name,
110        passed,
111        &detail,
112        &metrics,
113        started.elapsed().as_secs_f64(),
114        cleanup_ok,
115    );
116
117    if passed {
118        Ok(())
119    } else {
120        Err(boxed_error(detail))
121    }
122}