use std::fmt::Write as _;
use super::types::{SegmentKind, WorkflowCandidate};
use super::util::escape_harn_string;
pub fn generate_harn_code(candidate: &WorkflowCandidate) -> String {
let mut out = String::new();
let params = if candidate.parameters.is_empty() {
"task".to_string()
} else {
candidate
.parameters
.iter()
.map(|parameter| parameter.name.as_str())
.collect::<Vec<_>>()
.join(", ")
};
writeln!(out, "/**").unwrap();
writeln!(
out,
" * Generated by harn crystallize. Review before promotion."
)
.unwrap();
writeln!(out, " * Candidate: {}", candidate.id).unwrap();
writeln!(
out,
" * Source trace hashes: {}",
candidate.promotion.source_trace_hashes.join(", ")
)
.unwrap();
writeln!(
out,
" * Capabilities: {}",
if candidate.capabilities.is_empty() {
"none".to_string()
} else {
candidate.capabilities.join(", ")
}
)
.unwrap();
writeln!(
out,
" * Required secrets: {}",
if candidate.required_secrets.is_empty() {
"none".to_string()
} else {
candidate.required_secrets.join(", ")
}
)
.unwrap();
writeln!(out, " */").unwrap();
writeln!(out, "pipeline {}({}) {{", candidate.name, params).unwrap();
writeln!(out, " let review_warnings = []").unwrap();
for step in &candidate.steps {
writeln!(out, " // Step {}: {} {}", step.index, step.kind, step.name).unwrap();
for side_effect in &step.side_effects {
writeln!(
out,
" // side_effect: {} {}",
side_effect.kind, side_effect.target
)
.unwrap();
}
if let Some(approval) = &step.approval {
if approval.required {
writeln!(
out,
" // approval_required: {}",
approval
.boundary
.clone()
.unwrap_or_else(|| "human_review".to_string())
)
.unwrap();
}
}
if step.segment == SegmentKind::Fuzzy {
writeln!(
out,
" // TODO: fuzzy segment still needs LLM/reviewer handling before deterministic promotion."
)
.unwrap();
writeln!(
out,
" review_warnings.push(\"fuzzy step: {}\")",
escape_harn_string(&step.name)
)
.unwrap();
}
writeln!(
out,
" log(\"crystallized step {}: {}\")",
step.index,
escape_harn_string(&step.name)
)
.unwrap();
}
writeln!(
out,
" return {{status: \"shadow_ready\", candidate_id: \"{}\", review_warnings: review_warnings}}",
escape_harn_string(&candidate.id)
)
.unwrap();
writeln!(out, "}}").unwrap();
out
}
pub(super) fn rejected_workflow_stub(rejected: &[WorkflowCandidate]) -> String {
let mut out = String::new();
writeln!(
out,
"// Generated by harn crystallize. No safe candidate was proposed."
)
.unwrap();
writeln!(out, "pipeline crystallized_workflow(task) {{").unwrap();
writeln!(out, " log(\"no safe crystallization candidate\")").unwrap();
writeln!(
out,
" return {{status: \"rejected\", rejected_candidates: {}}}",
rejected.len()
)
.unwrap();
writeln!(out, "}}").unwrap();
out
}
pub fn generate_eval_pack(candidate: &WorkflowCandidate) -> String {
let mut out = String::new();
writeln!(out, "version = 1").unwrap();
writeln!(
out,
"id = \"{}-crystallization\"",
candidate.promotion.package_name
)
.unwrap();
writeln!(
out,
"name = \"{} crystallization shadow evals\"",
candidate.name
)
.unwrap();
writeln!(out).unwrap();
writeln!(out, "[package]").unwrap();
writeln!(out, "name = \"{}\"", candidate.promotion.package_name).unwrap();
writeln!(out, "version = \"{}\"", candidate.promotion.version).unwrap();
writeln!(out).unwrap();
for example in &candidate.examples {
writeln!(out, "[[fixtures]]").unwrap();
writeln!(out, "id = \"{}\"", example.trace_id).unwrap();
writeln!(out, "kind = \"jsonl-trace\"").unwrap();
writeln!(out, "trace_id = \"{}\"", example.trace_id).unwrap();
writeln!(out).unwrap();
}
writeln!(out, "[[rubrics]]").unwrap();
writeln!(out, "id = \"shadow-determinism\"").unwrap();
writeln!(out, "kind = \"deterministic\"").unwrap();
writeln!(out).unwrap();
writeln!(out, "[[rubrics.assertions]]").unwrap();
writeln!(out, "kind = \"crystallization-shadow\"").unwrap();
writeln!(
out,
"expected = {{ candidate_id = \"{}\", pass = true }}",
candidate.id
)
.unwrap();
writeln!(out).unwrap();
writeln!(out, "[[cases]]").unwrap();
writeln!(out, "id = \"{}-shadow\"", candidate.name).unwrap();
writeln!(out, "name = \"{} shadow replay\"", candidate.name).unwrap();
writeln!(out, "rubrics = [\"shadow-determinism\"]").unwrap();
writeln!(out, "severity = \"blocking\"").unwrap();
out
}