mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-09 03:20:56 +00:00
Improve summary:medium and summary:low preamble rendering
summary:medium now uses compact handler-specific rendering (script command/stdout/stderr, codergen model/files) instead of dumping raw context_updates. summary:low adds minimal handler info (type, script command, or model name) to each stage line. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
28f75f75dc
commit
7709d0a01c
2 changed files with 253 additions and 37 deletions
|
|
@ -405,17 +405,11 @@ fn build_summary_preamble(
|
|||
|
||||
append_filtered_context_table(&mut parts, context, &all_rendered_keys);
|
||||
}
|
||||
_ => {
|
||||
// Low and Medium share the same structure as before
|
||||
SummaryDetail::Medium => {
|
||||
let stage_count = completed_nodes.len();
|
||||
parts.push(format!("Completed {stage_count} stage(s) so far."));
|
||||
|
||||
let recent_count = match detail {
|
||||
SummaryDetail::Low => 2,
|
||||
SummaryDetail::Medium => 5,
|
||||
SummaryDetail::High => unreachable!(),
|
||||
};
|
||||
|
||||
let recent_count = 5;
|
||||
let stages_to_show: Vec<&String> = if stage_count > recent_count {
|
||||
let skipped = stage_count - recent_count;
|
||||
parts.push(format!("\n({skipped} earlier stage(s) omitted)"));
|
||||
|
|
@ -438,31 +432,83 @@ fn build_summary_preamble(
|
|||
}
|
||||
parts.push(line);
|
||||
|
||||
// For medium detail, include context updates from the outcome
|
||||
if matches!(detail, SummaryDetail::Medium)
|
||||
&& !outcome.context_updates.is_empty()
|
||||
{
|
||||
let mut update_keys: Vec<&String> =
|
||||
outcome.context_updates.keys().collect();
|
||||
update_keys.sort();
|
||||
for key in update_keys {
|
||||
if let Some(val) = outcome.context_updates.get(key) {
|
||||
parts.push(format!(
|
||||
" - set {key} = {}",
|
||||
format_value(val)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let node = graph.nodes.get(*node_id);
|
||||
let details =
|
||||
render_compact_stage_details(node_id, node, outcome);
|
||||
parts.extend(details);
|
||||
|
||||
all_rendered_keys
|
||||
.extend(stage_rendered_keys(node_id, outcome));
|
||||
} else {
|
||||
parts.push(format!("- {node_id}: completed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include context values for medium only (low excludes them)
|
||||
if matches!(detail, SummaryDetail::Medium) {
|
||||
append_filtered_context(&mut parts, context, &all_rendered_keys);
|
||||
append_filtered_context(&mut parts, context, &all_rendered_keys);
|
||||
}
|
||||
SummaryDetail::Low => {
|
||||
let stage_count = completed_nodes.len();
|
||||
parts.push(format!("Completed {stage_count} stage(s) so far."));
|
||||
|
||||
let recent_count = 2;
|
||||
let stages_to_show: Vec<&String> = if stage_count > recent_count {
|
||||
let skipped = stage_count - recent_count;
|
||||
parts.push(format!("\n({skipped} earlier stage(s) omitted)"));
|
||||
completed_nodes.iter().skip(skipped).collect()
|
||||
} else {
|
||||
completed_nodes.iter().collect()
|
||||
};
|
||||
|
||||
if !stages_to_show.is_empty() {
|
||||
parts.push(String::from("\nRecent stages:"));
|
||||
for node_id in &stages_to_show {
|
||||
if let Some(outcome) = node_outcomes.get(*node_id) {
|
||||
let status = outcome.status.to_string();
|
||||
let mut line = format!("- {node_id}: {status}");
|
||||
if let Some(notes) = outcome.notes.as_deref() {
|
||||
line.push_str(&format!(" ({notes})"));
|
||||
}
|
||||
if let Some(reason) = outcome.failure_reason.as_deref() {
|
||||
line.push_str(&format!(" [reason: {reason}]"));
|
||||
}
|
||||
parts.push(line);
|
||||
|
||||
let node = graph.nodes.get(*node_id);
|
||||
let handler =
|
||||
node.and_then(|n| n.handler_type());
|
||||
if let Some(h) = handler {
|
||||
parts.push(format!(" - Handler: {h}"));
|
||||
}
|
||||
match handler {
|
||||
Some("script") => {
|
||||
if let Some(n) = node {
|
||||
if let Some(cmd) = n
|
||||
.attrs
|
||||
.get("script")
|
||||
.or_else(|| n.attrs.get("tool_command"))
|
||||
.and_then(|v| v.as_str())
|
||||
{
|
||||
parts.push(format!(
|
||||
" - Script: `{cmd}`"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some("codergen") => {
|
||||
if let Some(usage) = &outcome.usage {
|
||||
parts.push(format!(
|
||||
" - Model: {}",
|
||||
usage.model
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
parts.push(format!("- {node_id}: completed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -917,7 +963,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn build_preamble_summary_low_shows_only_recent_stages() {
|
||||
let graph = Graph::new("test");
|
||||
let mut graph = Graph::new("test");
|
||||
let mut step3 = Node::new("step3");
|
||||
step3.attrs.insert(
|
||||
"shape".to_string(),
|
||||
AttrValue::String("parallelogram".to_string()),
|
||||
);
|
||||
graph.nodes.insert("step3".to_string(), step3);
|
||||
|
||||
let context = Context::new();
|
||||
let completed_nodes = vec![
|
||||
"step1".to_string(),
|
||||
|
|
@ -945,6 +998,101 @@ mod tests {
|
|||
assert!(preamble.contains("step3"), "should show recent stage");
|
||||
assert!(preamble.contains("step4"), "should show most recent stage");
|
||||
assert!(preamble.contains("omitted"), "should indicate omitted stages");
|
||||
// Handler type should appear for nodes with known handlers
|
||||
assert!(
|
||||
preamble.contains("Handler: script"),
|
||||
"should show handler type for step3"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summary_low_script_stage_shows_handler_and_command() {
|
||||
let mut graph = Graph::new("test");
|
||||
let mut run_tests = Node::new("run_tests");
|
||||
run_tests.attrs.insert(
|
||||
"shape".to_string(),
|
||||
AttrValue::String("parallelogram".to_string()),
|
||||
);
|
||||
run_tests.attrs.insert(
|
||||
"script".to_string(),
|
||||
AttrValue::String("cargo test".to_string()),
|
||||
);
|
||||
graph.nodes.insert("run_tests".to_string(), run_tests);
|
||||
|
||||
let context = Context::new();
|
||||
let completed_nodes = vec!["run_tests".to_string()];
|
||||
let mut node_outcomes: HashMap<String, Outcome> = HashMap::new();
|
||||
let mut outcome = Outcome::fail("exit code 1");
|
||||
outcome.context_updates.insert(
|
||||
"script.output".to_string(),
|
||||
serde_json::json!("test failed"),
|
||||
);
|
||||
node_outcomes.insert("run_tests".to_string(), outcome);
|
||||
|
||||
let preamble = build_preamble(
|
||||
"summary:low",
|
||||
&context,
|
||||
&graph,
|
||||
&completed_nodes,
|
||||
&node_outcomes,
|
||||
);
|
||||
|
||||
assert!(
|
||||
preamble.contains("Handler: script"),
|
||||
"should show handler type"
|
||||
);
|
||||
assert!(
|
||||
preamble.contains("Script: `cargo test`"),
|
||||
"should show script command"
|
||||
);
|
||||
// Low mode should NOT include stdout/stderr
|
||||
assert!(
|
||||
!preamble.contains("Stdout:"),
|
||||
"should not show stdout in low mode"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summary_low_codergen_stage_shows_handler_and_model() {
|
||||
let mut graph = Graph::new("test");
|
||||
let mut report = Node::new("report");
|
||||
report.attrs.insert(
|
||||
"shape".to_string(),
|
||||
AttrValue::String("box".to_string()),
|
||||
);
|
||||
graph.nodes.insert("report".to_string(), report);
|
||||
|
||||
let context = Context::new();
|
||||
let completed_nodes = vec!["report".to_string()];
|
||||
let mut node_outcomes: HashMap<String, Outcome> = HashMap::new();
|
||||
let mut outcome = Outcome::success();
|
||||
outcome.usage = Some(StageUsage {
|
||||
model: "claude-sonnet-4-20250514".to_string(),
|
||||
input_tokens: 1000,
|
||||
output_tokens: 200,
|
||||
cache_read_tokens: None,
|
||||
cache_write_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
cost: None,
|
||||
});
|
||||
node_outcomes.insert("report".to_string(), outcome);
|
||||
|
||||
let preamble = build_preamble(
|
||||
"summary:low",
|
||||
&context,
|
||||
&graph,
|
||||
&completed_nodes,
|
||||
&node_outcomes,
|
||||
);
|
||||
|
||||
assert!(
|
||||
preamble.contains("Handler: codergen"),
|
||||
"should show handler type"
|
||||
);
|
||||
assert!(
|
||||
preamble.contains("Model: claude-sonnet-4-20250514"),
|
||||
"should show model name"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1033,16 +1181,31 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn build_preamble_summary_medium_includes_context_updates() {
|
||||
let graph = Graph::new("test");
|
||||
fn build_preamble_summary_medium_uses_compact_handler_details() {
|
||||
let mut graph = Graph::new("test");
|
||||
let mut run_tests = Node::new("run_tests");
|
||||
run_tests.attrs.insert(
|
||||
"shape".to_string(),
|
||||
AttrValue::String("parallelogram".to_string()),
|
||||
);
|
||||
run_tests.attrs.insert(
|
||||
"script".to_string(),
|
||||
AttrValue::String("make test".to_string()),
|
||||
);
|
||||
graph.nodes.insert("run_tests".to_string(), run_tests);
|
||||
|
||||
let context = Context::new();
|
||||
let completed_nodes = vec!["work".to_string()];
|
||||
let completed_nodes = vec!["run_tests".to_string()];
|
||||
let mut node_outcomes: HashMap<String, Outcome> = HashMap::new();
|
||||
let mut outcome = Outcome::success();
|
||||
outcome.context_updates.insert(
|
||||
"script.output".to_string(),
|
||||
serde_json::json!("All tests passed\n"),
|
||||
);
|
||||
outcome
|
||||
.context_updates
|
||||
.insert("result.score".to_string(), serde_json::json!(95));
|
||||
node_outcomes.insert("work".to_string(), outcome);
|
||||
.insert("script.stderr".to_string(), serde_json::json!(""));
|
||||
node_outcomes.insert("run_tests".to_string(), outcome);
|
||||
|
||||
let preamble = build_preamble(
|
||||
"summary:medium",
|
||||
|
|
@ -1053,8 +1216,60 @@ mod tests {
|
|||
);
|
||||
|
||||
assert!(
|
||||
preamble.contains("result.score"),
|
||||
"should include context updates from outcomes"
|
||||
preamble.contains("Script: `make test`"),
|
||||
"should show script command via compact renderer"
|
||||
);
|
||||
assert!(
|
||||
preamble.contains("All tests passed"),
|
||||
"should show stdout via compact renderer"
|
||||
);
|
||||
assert!(
|
||||
!preamble.contains("set script.output"),
|
||||
"should not dump raw context updates"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn summary_medium_codergen_stage_shows_compact_details() {
|
||||
let mut graph = Graph::new("test");
|
||||
let mut report = Node::new("report");
|
||||
report.attrs.insert(
|
||||
"shape".to_string(),
|
||||
AttrValue::String("box".to_string()),
|
||||
);
|
||||
graph.nodes.insert("report".to_string(), report);
|
||||
|
||||
let context = Context::new();
|
||||
let completed_nodes = vec!["report".to_string()];
|
||||
let mut node_outcomes: HashMap<String, Outcome> = HashMap::new();
|
||||
let mut outcome = Outcome::success();
|
||||
outcome.usage = Some(StageUsage {
|
||||
model: "claude-sonnet-4-20250514".to_string(),
|
||||
input_tokens: 1500,
|
||||
output_tokens: 300,
|
||||
cache_read_tokens: None,
|
||||
cache_write_tokens: None,
|
||||
reasoning_tokens: None,
|
||||
cost: None,
|
||||
});
|
||||
outcome.files_touched = vec!["src/lib.rs".to_string()];
|
||||
node_outcomes.insert("report".to_string(), outcome);
|
||||
|
||||
let preamble = build_preamble(
|
||||
"summary:medium",
|
||||
&context,
|
||||
&graph,
|
||||
&completed_nodes,
|
||||
&node_outcomes,
|
||||
);
|
||||
|
||||
assert!(
|
||||
preamble.contains("claude-sonnet-4-20250514"),
|
||||
"should show model name"
|
||||
);
|
||||
assert!(
|
||||
preamble.contains("src/lib.rs"),
|
||||
"should show files touched"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6784,10 +6784,11 @@ async fn fidelity_prompt_summary_low() {
|
|||
async fn fidelity_prompt_summary_medium() {
|
||||
let prompt = run_fidelity_prompt_pipeline("summary:medium").await;
|
||||
|
||||
// summary:medium includes goal, stages, and context values
|
||||
// summary:medium includes goal, stages, and compact handler details
|
||||
assert!(prompt.contains("Validate the build"), "summary:medium: should contain goal");
|
||||
assert!(prompt.contains("run_tests"), "summary:medium: should mention run_tests");
|
||||
assert!(prompt.contains("## Context"), "summary:medium: should include context section");
|
||||
assert!(prompt.contains("Script:"), "summary:medium: should show script sub-item for run_tests");
|
||||
assert!(prompt.contains("Stdout:"), "summary:medium: should show stdout sub-item for run_tests");
|
||||
|
||||
// Original prompt at the end
|
||||
assert!(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue