use std::sync::Arc;
use rsclaw::{config, provider::build::build_providers, skill::workflow_distill::crystallize_workflow};
use rsclaw_types::turn_metrics::TurnMetrics;
#[tokio::test]
#[ignore = "hits a real LLM + network; run with --ignored"]
async fn evolution_generates_skill_from_hard_turn() {
let cfg = config::load().expect("load config");
let providers = Arc::new(build_providers(&cfg));
let model =
std::env::var("DISTILL_MODEL").unwrap_or_else(|_| "rsclaw/rsclaw-agent-v1".to_owned());
eprintln!("[e2e] distill model = {model}");
let mut m = TurnMetrics::new();
m.record_tool(
"web_fetch",
"url=api.example.com/odds".into(),
"200 OK, 4KB json".into(),
false,
);
m.record_tool(
"web_fetch",
"url=api.example.com/fixtures".into(),
"timeout after 20s".into(),
true,
);
m.record_tool(
"web_fetch",
"url=api.example.com/fixtures (retry, 60s timeout)".into(),
"200 OK".into(),
false,
);
m.record_tool(
"image_gen",
"prompt=match poster, size=1024x1024".into(),
"wrote /tmp/poster.png".into(),
false,
);
m.final_text_len = 800;
let user_text = "查一下今晚世界杯墨西哥vs南非的赔率和赛程,再给我做一张比赛海报";
let reply_text = "赔率/赛程已取(fixtures 接口要 60s 超时,20s 会失败),海报已生成。";
let skills_dir = std::env::temp_dir().join("rsclaw_skill_evo_e2e");
let _ = std::fs::remove_dir_all(&skills_dir);
std::fs::create_dir_all(&skills_dir).expect("mkdir skills_dir");
let result = crystallize_workflow(
user_text,
reply_text,
&m,
0xDEAD_BEEF,
&providers,
&model,
&skills_dir,
)
.await
.expect("crystallize_workflow should not hard-error");
let path = result.expect(
"expected a generated SKILL.md path (None = evolution disabled, no model, \
or the LLM output failed validation twice — check logs above)",
);
eprintln!("[e2e] generated skill at: {}", path.display());
let body = std::fs::read_to_string(&path).expect("read generated SKILL.md");
eprintln!("\n----- generated SKILL.md -----\n{body}\n------------------------------");
assert!(body.starts_with("---"), "must start with YAML frontmatter");
assert!(body.contains("name:"), "frontmatter must have name:");
assert!(body.contains("description:"), "frontmatter must have description:");
assert!(
path.file_name().unwrap().to_string_lossy() == "SKILL.md",
"file must be named SKILL.md"
);
assert!(
path.parent()
.unwrap()
.file_name()
.unwrap()
.to_string_lossy()
.starts_with("auto-"),
"generated skill dir should be auto-prefixed"
);
}